From 96080bdfaf62889a83d3cb62e161674d03895e9b Mon Sep 17 00:00:00 2001 From: Elise Chouleur Date: Fri, 26 Sep 2025 16:56:13 +0200 Subject: [PATCH 1/2] Adapt to be able to build with msvc toolchain: need further work especially for bindgen i32/u32 issue --- Dockerfile | 2 +- build.rs | 17 +++++++++++++---- .../cblite.dll | Bin .../cblite.lib | Bin .../cblite.stripped.dll | Bin src/error.rs | 4 ++-- src/fleece.rs | 2 +- src/fleece_mutable.rs | 4 ++-- src/replicator.rs | 15 +++++++++------ update_cblite_c.sh | 2 +- 10 files changed, 29 insertions(+), 17 deletions(-) rename libcblite_enterprise/lib/{x86_64-pc-windows-gnu => windows}/cblite.dll (100%) rename libcblite_enterprise/lib/{x86_64-pc-windows-gnu => windows}/cblite.lib (100%) rename libcblite_enterprise/lib/{x86_64-pc-windows-gnu => windows}/cblite.stripped.dll (100%) mode change 100755 => 100644 diff --git a/Dockerfile b/Dockerfile index 90352d7..e00b39a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN strip /build/${DIRNAME}/lib/x86_64-linux-android/libcblite.so -o /build/${DI RUN strip /build/${DIRNAME}/lib/i686-linux-android/libcblite.so -o /build/${DIRNAME}/lib/i686-linux-android/libcblite.stripped.so RUN /usr/aarch64-linux-gnu/bin/strip /build/${DIRNAME}/lib/aarch64-linux-android/libcblite.so -o /build/${DIRNAME}/lib/aarch64-linux-android/libcblite.stripped.so RUN /usr/aarch64-linux-gnu/bin/strip /build/${DIRNAME}/lib/arm-linux-androideabi/libcblite.so -o /build/${DIRNAME}/lib/arm-linux-androideabi/libcblite.stripped.so -RUN strip /build/${DIRNAME}/lib/x86_64-pc-windows-gnu/cblite.dll -o /build/${DIRNAME}/lib/x86_64-pc-windows-gnu/cblite.stripped.dll +RUN strip /build/${DIRNAME}/lib/windows/cblite.dll -o /build/${DIRNAME}/lib/windows/cblite.stripped.dll FROM scratch AS strip COPY --from=strip-stage /build/${DIRNAME}/ . diff --git a/build.rs b/build.rs index 9205646..c83753b 100644 --- a/build.rs +++ b/build.rs @@ -172,6 +172,10 @@ fn generate_bindings() -> Result<(), Box> { .clang_arg(format!("--target={}", "x86_64-pc-windows-gnu")); } + bindings = bindings + .clang_arg("-IC:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC\\14.40.33807\\include".to_string()) + .clang_arg("-IC:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17763.0\\ucrt"); + let out_dir = env::var("OUT_DIR")?; bindings .allowlist_type("CBL.*") @@ -221,6 +225,13 @@ fn configure_rustc() -> Result<(), Box> { env!("CARGO_MANIFEST_DIR"), CBL_LIB_DIR ); + } else if target_os == "windows" { + println!("cargo:rustc-link-lib=dylib=cblite"); + println!( + "cargo:rustc-link-search={}/{}/windows", + env!("CARGO_MANIFEST_DIR"), + CBL_LIB_DIR + ); } else { println!("cargo:rustc-link-lib=dylib=cblite"); println!( @@ -239,10 +250,8 @@ pub fn copy_lib() -> Result<(), Box> { "{}/{}/{}/", env!("CARGO_MANIFEST_DIR"), CBL_LIB_DIR, - if target_os == "ios" { - "ios".to_string() - } else if target_os == "macos" { - "macos".to_string() + if target_os == "ios" || target_os == "macos" || target_os == "windows" { + target_os.clone() } else { env::var("TARGET").unwrap() } diff --git a/libcblite_enterprise/lib/x86_64-pc-windows-gnu/cblite.dll b/libcblite_enterprise/lib/windows/cblite.dll similarity index 100% rename from libcblite_enterprise/lib/x86_64-pc-windows-gnu/cblite.dll rename to libcblite_enterprise/lib/windows/cblite.dll diff --git a/libcblite_enterprise/lib/x86_64-pc-windows-gnu/cblite.lib b/libcblite_enterprise/lib/windows/cblite.lib similarity index 100% rename from libcblite_enterprise/lib/x86_64-pc-windows-gnu/cblite.lib rename to libcblite_enterprise/lib/windows/cblite.lib diff --git a/libcblite_enterprise/lib/x86_64-pc-windows-gnu/cblite.stripped.dll b/libcblite_enterprise/lib/windows/cblite.stripped.dll old mode 100755 new mode 100644 similarity index 100% rename from libcblite_enterprise/lib/x86_64-pc-windows-gnu/cblite.stripped.dll rename to libcblite_enterprise/lib/windows/cblite.stripped.dll diff --git a/src/error.rs b/src/error.rs index 8d3f851..6f4d13f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -212,7 +212,7 @@ impl Error { } pub(crate) fn as_cbl_error(&self) -> CBLError { - let domain: u32; + let domain: i32; let code: i32; match &self.code { ErrorCode::CouchbaseLite(e) => { @@ -278,7 +278,7 @@ impl fmt::Display for Error { impl ErrorCode { fn new(err: &CBLError) -> Self { - match u32::from(err.domain) { + match i32::from(err.domain) { kCBLDomain => CouchbaseLiteError::from_i32(err.code) .map_or(Self::untranslatable(), Self::CouchbaseLite), kCBLNetworkDomain => { diff --git a/src/fleece.rs b/src/fleece.rs index 3045514..4b5b5de 100644 --- a/src/fleece.rs +++ b/src/fleece.rs @@ -71,7 +71,7 @@ impl Fleece { pub fn parse(data: &[u8], trust: Trust) -> Result { unsafe { let copied = FLSlice_Copy(from_bytes(data).get_ref()); - let doc = FLDoc_FromResultData(copied, trust as u32, ptr::null_mut(), NULL_SLICE); + let doc = FLDoc_FromResultData(copied, trust as i32, ptr::null_mut(), NULL_SLICE); if doc.is_null() { return Err(Error::fleece_error(FLError_kFLInvalidData)); } diff --git a/src/fleece_mutable.rs b/src/fleece_mutable.rs index 876351f..03e834b 100644 --- a/src/fleece_mutable.rs +++ b/src/fleece_mutable.rs @@ -80,7 +80,7 @@ impl MutableArray { pub fn from_array_(array: &Array, flags: CopyFlags) -> Self { unsafe { Self { - cbl_ref: FLArray_MutableCopy(array.get_ref(), flags as u32), + cbl_ref: FLArray_MutableCopy(array.get_ref(), flags as i32), } } } @@ -274,7 +274,7 @@ impl MutableDict { pub fn from_dict_(dict: &Dict, flags: CopyFlags) -> Self { unsafe { Self { - cbl_ref: FLDict_MutableCopy(dict.get_ref(), flags as u32), + cbl_ref: FLDict_MutableCopy(dict.get_ref(), flags as i32), } } } diff --git a/src/replicator.rs b/src/replicator.rs index fdbb94e..ed900e1 100644 --- a/src/replicator.rs +++ b/src/replicator.rs @@ -170,7 +170,7 @@ pub enum ReplicatorType { impl From for ReplicatorType { fn from(repl_type: CBLReplicatorType) -> Self { - match u32::from(repl_type) { + match i32::from(repl_type) { kCBLReplicatorTypePushAndPull => Self::PushAndPull, kCBLReplicatorTypePush => Self::Push, kCBLReplicatorTypePull => Self::Pull, @@ -197,7 +197,7 @@ pub enum ProxyType { impl From for ProxyType { fn from(proxy_type: CBLProxyType) -> Self { - match u32::from(proxy_type) { + match i32::from(proxy_type) { kCBLProxyHTTP => Self::HTTP, kCBLProxyHTTPS => Self::HTTPS, _ => unreachable!(), @@ -297,7 +297,10 @@ unsafe extern "C" fn c_replication_pull_filter( } } fn read_document_flags(flags: CBLDocumentFlags) -> (bool, bool) { - (flags & DELETED != 0, flags & ACCESS_REMOVED != 0) + ( + flags & DELETED as u32 != 0, + flags & ACCESS_REMOVED as u32 != 0, + ) } /** Conflict-resolution callback for use in replications. This callback will be invoked @@ -1085,7 +1088,7 @@ pub enum ReplicatorActivityLevel { impl From for ReplicatorActivityLevel { fn from(level: u8) -> Self { - match u32::from(level) { + match i32::from(level) { kCBLReplicatorStopped => Self::Stopped, kCBLReplicatorOffline => Self::Offline, kCBLReplicatorConnecting => Self::Connecting, @@ -1178,8 +1181,8 @@ unsafe extern "C" fn c_replicator_document_change_listener( } /** Flags describing a replicated document. */ -pub static DELETED: u32 = kCBLDocumentFlagsDeleted; -pub static ACCESS_REMOVED: u32 = kCBLDocumentFlagsAccessRemoved; +pub static DELETED: i32 = kCBLDocumentFlagsDeleted; +pub static ACCESS_REMOVED: i32 = kCBLDocumentFlagsAccessRemoved; /** Information about a document that's been pushed or pulled. */ pub struct ReplicatedDocument { diff --git a/update_cblite_c.sh b/update_cblite_c.sh index 29ba685..3d7f982 100755 --- a/update_cblite_c.sh +++ b/update_cblite_c.sh @@ -183,7 +183,7 @@ do ;; windows) - platformFolder="${libFolder}/x86_64-pc-windows-gnu" + platformFolder="${libFolder}/windows" mkdir $platformFolder libFile="${unzipPlatformFolder}/libcblite-${version}/lib/cblite.lib" From adb1544dddc6327c2ce4a6e4813ec85610c509cf Mon Sep 17 00:00:00 2001 From: xdm67x Date: Fri, 26 Sep 2025 17:51:31 +0200 Subject: [PATCH 2/2] build: try with forced types and configuration on msvc --- build.rs | 219 +++++++++++++++++++++++++++++++++++++++++- src/error.rs | 4 +- src/fleece.rs | 2 +- src/fleece_mutable.rs | 4 +- src/replicator.rs | 15 ++- 5 files changed, 225 insertions(+), 19 deletions(-) diff --git a/build.rs b/build.rs index c83753b..1f7dc7a 100644 --- a/build.rs +++ b/build.rs @@ -41,6 +41,38 @@ use std::path::PathBuf; use std::process::Command; use fs_extra::dir; +// Custom callback to handle MSVC type mapping issues +#[derive(Debug)] +struct MSVCTypeCallback; + +impl bindgen::callbacks::ParseCallbacks for MSVCTypeCallback { + fn int_macro(&self, name: &str, value: i64) -> Option { + // Force unsigned macros and values to use u32 on MSVC + if name.contains("UINT") + || name.ends_with("_U") + || (value >= 0 && name.contains("UNSIGNED")) + { + Some(bindgen::callbacks::IntKind::U32) + } else { + None + } + } + + fn item_name(&self, item_info: bindgen::callbacks::ItemInfo<'_>) -> Option { + // Handle specific type mappings if needed + match item_info.name { + "c_uint" => Some("u32".to_string()), + "c_ulong" => Some("u32".to_string()), + _ => None, + } + } + + // Chain with CargoCallbacks + fn include_file(&self, filename: &str) { + bindgen::CargoCallbacks::new().include_file(filename); + } +} + #[cfg(feature = "community")] static CBL_INCLUDE_DIR: &str = "libcblite_community/include"; #[cfg(feature = "enterprise")] @@ -103,6 +135,139 @@ enum OperatingSystem { IOs, } +fn find_msvc_paths() -> Result<(Option, Option), Box> { + // Try to find MSVC installation paths automatically + let mut msvc_include = None; + let mut ucrt_include = None; + + // Method 1: Use environment variables from Rust/Cargo build (preferred method) + if let Ok(include_path) = env::var("INCLUDE") { + // Parse the INCLUDE environment variable that MSVC sets + for path in include_path.split(';') { + let path = path.trim(); + if !path.is_empty() { + if path.contains("VC\\Tools\\MSVC") && path.ends_with("include") { + msvc_include = Some(path.to_string()); + } else if path.contains("Windows Kits") && path.ends_with("ucrt") { + ucrt_include = Some(path.to_string()); + } + } + } + } + + // Method 2: Try using vswhere.exe if available and no env vars found + if msvc_include.is_none() + && let Ok(output) = Command::new("vswhere.exe") + .args([ + "-latest", + "-products", + "*", + "-requires", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", + "installationPath", + ]) + .output() + && output.status.success() + { + let vs_path = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if !vs_path.is_empty() { + // Try to find the MSVC version + let vc_tools_path = format!("{}\\VC\\Tools\\MSVC", vs_path); + if let Ok(entries) = fs::read_dir(&vc_tools_path) { + let mut versions = Vec::new(); + for entry in entries.flatten() { + if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + let version_path = entry.path(); + let include_path = version_path.join("include"); + if include_path.exists() { + let version = entry.file_name().to_string_lossy().to_string(); + versions.push((version, include_path.to_string_lossy().to_string())); + } + } + } + // Sort versions and take the latest + versions.sort_by(|a, b| b.0.cmp(&a.0)); + if let Some((_, path)) = versions.first() { + msvc_include = Some(path.clone()); + } + } + } + } + + // Method 3: Check common Visual Studio locations + if msvc_include.is_none() { + let common_vs_paths = [ + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC", + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Tools\\MSVC", + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC", + "C:\\Program Files\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Tools\\MSVC", + "C:\\Program Files\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC", + "C:\\Program Files\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC", + ]; + + for vs_path in &common_vs_paths { + if let Ok(entries) = fs::read_dir(vs_path) { + let mut versions = Vec::new(); + for entry in entries.flatten() { + if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + let version_path = entry.path(); + let include_path = version_path.join("include"); + if include_path.exists() { + let version = entry.file_name().to_string_lossy().to_string(); + versions.push((version, include_path.to_string_lossy().to_string())); + } + } + } + // Sort versions and take the latest + versions.sort_by(|a, b| b.0.cmp(&a.0)); + if let Some((_, path)) = versions.first() { + msvc_include = Some(path.clone()); + break; + } + } + } + } + + // Method 4: Try to find Windows SDK UCRT if not found via env vars + if ucrt_include.is_none() { + let sdk_paths = [ + "C:\\Program Files (x86)\\Windows Kits\\10\\Include", + "C:\\Program Files\\Windows Kits\\10\\Include", + ]; + + for sdk_path in &sdk_paths { + if let Ok(entries) = fs::read_dir(sdk_path) { + // Find the latest version + let mut versions = Vec::new(); + for entry in entries.flatten() { + if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + let name = entry.file_name(); + let name_str = name.to_string_lossy(); + if name_str.starts_with("10.") { + let ucrt_path = entry.path().join("ucrt"); + if ucrt_path.exists() { + versions.push(( + name_str.to_string(), + ucrt_path.to_string_lossy().to_string(), + )); + } + } + } + } + // Sort versions and take the latest + versions.sort_by(|a, b| b.0.cmp(&a.0)); + if let Some((_, path)) = versions.first() { + ucrt_include = Some(path.clone()); + break; + } + } + } + } + + Ok((msvc_include, ucrt_include)) +} + fn is_target(target: OperatingSystem) -> Result> { let target_os = env::var("CARGO_CFG_TARGET_OS")?; Ok(match target { @@ -172,12 +337,39 @@ fn generate_bindings() -> Result<(), Box> { .clang_arg(format!("--target={}", "x86_64-pc-windows-gnu")); } - bindings = bindings - .clang_arg("-IC:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC\\14.40.33807\\include".to_string()) - .clang_arg("-IC:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17763.0\\ucrt"); + // Special handling for MSVC targets to fix unsigned type generation + if is_target(OperatingSystem::Windows)? { + // Try to auto-detect MSVC paths + let (msvc_include, ucrt_include) = find_msvc_paths()?; + + if let Some(msvc_path) = msvc_include { + bindings = bindings.clang_arg(format!("-I{}", msvc_path)); + } else { + eprintln!("Warning: Could not auto-detect MSVC include path, using fallback"); + bindings = bindings.clang_arg("-IC:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC\\14.40.33807\\include"); + } + + if let Some(ucrt_path) = ucrt_include { + bindings = bindings.clang_arg(format!("-I{}", ucrt_path)); + } else { + eprintln!("Warning: Could not auto-detect Windows SDK UCRT path, using fallback"); + bindings = bindings.clang_arg( + "-IC:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.17763.0\\ucrt", + ); + } + + bindings = bindings + // Force unsigned types on MSVC + .clang_arg("-DUINT32=unsigned int") + .clang_arg("-DULONG=unsigned long") + // Ensure proper type detection + .clang_arg("-D_WIN32_WINNT=0x0601") + .blocklist_type("c_uint") + .blocklist_type("c_ulong"); + } let out_dir = env::var("OUT_DIR")?; - bindings + let mut final_bindings = bindings .allowlist_type("CBL.*") .allowlist_type("FL.*") .allowlist_var("k?CBL.*") @@ -186,7 +378,24 @@ fn generate_bindings() -> Result<(), Box> { .allowlist_function("_?FL.*") .no_copy("FLSliceResult") .size_t_is_usize(true) - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + // Fix for MSVC: Force unsigned types to be generated as u32 instead of i32 + .default_enum_style(bindgen::EnumVariation::Consts) + // Force constants to be generated as const u32 instead of complex enums + .translate_enum_integer_types(true); + + // Add MSVC-specific type fixes + if is_target(OperatingSystem::Windows)? { + final_bindings = final_bindings + .raw_line("#[allow(non_camel_case_types)]") + .raw_line("pub type c_uint = u32;") + .raw_line("pub type c_ulong = u32;") + .raw_line("pub type DWORD = u32;") + .raw_line("pub type UINT = u32;") + .raw_line("pub type ULONG = u32;"); + } + + final_bindings + .parse_callbacks(Box::new(MSVCTypeCallback)) .generate() .expect("Unable to generate bindings") .write_to_file(PathBuf::from(out_dir).join("bindings.rs")) diff --git a/src/error.rs b/src/error.rs index 6f4d13f..8d3f851 100644 --- a/src/error.rs +++ b/src/error.rs @@ -212,7 +212,7 @@ impl Error { } pub(crate) fn as_cbl_error(&self) -> CBLError { - let domain: i32; + let domain: u32; let code: i32; match &self.code { ErrorCode::CouchbaseLite(e) => { @@ -278,7 +278,7 @@ impl fmt::Display for Error { impl ErrorCode { fn new(err: &CBLError) -> Self { - match i32::from(err.domain) { + match u32::from(err.domain) { kCBLDomain => CouchbaseLiteError::from_i32(err.code) .map_or(Self::untranslatable(), Self::CouchbaseLite), kCBLNetworkDomain => { diff --git a/src/fleece.rs b/src/fleece.rs index 4b5b5de..3045514 100644 --- a/src/fleece.rs +++ b/src/fleece.rs @@ -71,7 +71,7 @@ impl Fleece { pub fn parse(data: &[u8], trust: Trust) -> Result { unsafe { let copied = FLSlice_Copy(from_bytes(data).get_ref()); - let doc = FLDoc_FromResultData(copied, trust as i32, ptr::null_mut(), NULL_SLICE); + let doc = FLDoc_FromResultData(copied, trust as u32, ptr::null_mut(), NULL_SLICE); if doc.is_null() { return Err(Error::fleece_error(FLError_kFLInvalidData)); } diff --git a/src/fleece_mutable.rs b/src/fleece_mutable.rs index 03e834b..876351f 100644 --- a/src/fleece_mutable.rs +++ b/src/fleece_mutable.rs @@ -80,7 +80,7 @@ impl MutableArray { pub fn from_array_(array: &Array, flags: CopyFlags) -> Self { unsafe { Self { - cbl_ref: FLArray_MutableCopy(array.get_ref(), flags as i32), + cbl_ref: FLArray_MutableCopy(array.get_ref(), flags as u32), } } } @@ -274,7 +274,7 @@ impl MutableDict { pub fn from_dict_(dict: &Dict, flags: CopyFlags) -> Self { unsafe { Self { - cbl_ref: FLDict_MutableCopy(dict.get_ref(), flags as i32), + cbl_ref: FLDict_MutableCopy(dict.get_ref(), flags as u32), } } } diff --git a/src/replicator.rs b/src/replicator.rs index ed900e1..fdbb94e 100644 --- a/src/replicator.rs +++ b/src/replicator.rs @@ -170,7 +170,7 @@ pub enum ReplicatorType { impl From for ReplicatorType { fn from(repl_type: CBLReplicatorType) -> Self { - match i32::from(repl_type) { + match u32::from(repl_type) { kCBLReplicatorTypePushAndPull => Self::PushAndPull, kCBLReplicatorTypePush => Self::Push, kCBLReplicatorTypePull => Self::Pull, @@ -197,7 +197,7 @@ pub enum ProxyType { impl From for ProxyType { fn from(proxy_type: CBLProxyType) -> Self { - match i32::from(proxy_type) { + match u32::from(proxy_type) { kCBLProxyHTTP => Self::HTTP, kCBLProxyHTTPS => Self::HTTPS, _ => unreachable!(), @@ -297,10 +297,7 @@ unsafe extern "C" fn c_replication_pull_filter( } } fn read_document_flags(flags: CBLDocumentFlags) -> (bool, bool) { - ( - flags & DELETED as u32 != 0, - flags & ACCESS_REMOVED as u32 != 0, - ) + (flags & DELETED != 0, flags & ACCESS_REMOVED != 0) } /** Conflict-resolution callback for use in replications. This callback will be invoked @@ -1088,7 +1085,7 @@ pub enum ReplicatorActivityLevel { impl From for ReplicatorActivityLevel { fn from(level: u8) -> Self { - match i32::from(level) { + match u32::from(level) { kCBLReplicatorStopped => Self::Stopped, kCBLReplicatorOffline => Self::Offline, kCBLReplicatorConnecting => Self::Connecting, @@ -1181,8 +1178,8 @@ unsafe extern "C" fn c_replicator_document_change_listener( } /** Flags describing a replicated document. */ -pub static DELETED: i32 = kCBLDocumentFlagsDeleted; -pub static ACCESS_REMOVED: i32 = kCBLDocumentFlagsAccessRemoved; +pub static DELETED: u32 = kCBLDocumentFlagsDeleted; +pub static ACCESS_REMOVED: u32 = kCBLDocumentFlagsAccessRemoved; /** Information about a document that's been pushed or pulled. */ pub struct ReplicatedDocument {