diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..038e866 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,5 @@ +[target.x86_64-pc-windows-msvc] +image = "ghcr.io/rust-cross/cargo-xwin:latest" + +[target.aarch64-pc-windows-msvc] +image = "ghcr.io/rust-cross/cargo-xwin:latest" diff --git a/build.rs b/build.rs index 00c44fe..f0c14ff 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,7 @@ extern crate bindgen; use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::fmt::Debug; @@ -14,6 +14,37 @@ use regex::Regex; use std::collections::HashMap; use std::fmt::{Display, Formatter}; +fn find_static_lib(dir: &Path, base_name: &str, target_is_windows: bool) -> Option { + if !dir.is_dir() { + return None; + } + let prefixes = ["", "lib"]; + let extensions = if target_is_windows { + &[".lib", ".a"][..] + } else { + &[".a", ".lib"][..] + }; + for prefix in prefixes { + for ext in extensions { + let name = format!("{}{}{}", prefix, base_name, ext); + let path = dir.join(&name); + if path.exists() { + return Some(path); + } + } + } + None +} + +fn lib_name_from_path(lib_path: &Path) -> Result> { + let file_name = lib_path + .file_stem() + .ok_or(Error::msg("cannot get file stem"))? + .to_str() + .ok_or(Error::msg("cannot into string"))?; + Ok(file_name.to_string()) +} + struct Error(String); impl Debug for Error { @@ -36,15 +67,62 @@ impl Error { } } +fn fix_xwin_lib_case() -> Result<(), Box> { + let toolchain_key = format!( + "CMAKE_TOOLCHAIN_FILE_{}", + env::var("TARGET")?.replace('-', "_") + ); + let toolchain_file = env::var(&toolchain_key) + .or_else(|_| env::var("TARGET_CMAKE_TOOLCHAIN_FILE")) + .or_else(|_| env::var("CMAKE_TOOLCHAIN_FILE"))?; + + let toolchain_path = PathBuf::from(&toolchain_file); + let cargo_xwin_base = toolchain_path + .parent() + .and_then(|p| p.parent()) + .and_then(|p| p.parent()) + .ok_or(Error::msg("cannot determine cargo-xwin base dir"))?; + + let xwin_sdk_lib = cargo_xwin_base.join("xwin/sdk/lib/um/x86_64"); + if !xwin_sdk_lib.exists() { + return Ok(()); + } + + #[cfg(unix)] + { + use std::os::unix::fs::symlink; + let fixes = [ + ("Windowscodecs.lib", "WINDOWSCODECS.lib"), + ("Windowscodecs.lib", "windowscodecs.lib"), + ]; + for (link_name, target_name) in &fixes { + let target = xwin_sdk_lib.join(target_name); + let link = xwin_sdk_lib.join(link_name); + if target.exists() && !link.exists() { + let _ = symlink(&target, &link); + } + } + } + + Ok(()) +} + fn main() -> Result<(), Box> { if env::var("DOCS_RS").is_err() { + let host = env::var("HOST")?; + let target = env::var("TARGET")?; + let is_cross = host != target; + let is_windows = target.contains("windows"); + let _is_macos = target.contains("apple"); + let libczi_dir = PathBuf::from("libczi"); let libczi_src = libczi_dir.join("Src/libCZI"); let libcziapi_inc = libczi_dir.join("Src/libCZIAPI/inc"); let libcziapi_src = libczi_dir.join("Src/libCZIAPI/src"); let libcziapi_h = libcziapi_inc.join("libCZIApi.h"); - let dst = cmake::Config::new(&libczi_dir) + let mut cmake_config = cmake::Config::new(&libczi_dir); + cmake_config .cxxflag("-fms-extensions") .define("LIBCZI_BUILD_UNITTESTS", "OFF") .define("LIBCZI_BUILD_CZICMD", "OFF") @@ -55,16 +133,54 @@ fn main() -> Result<(), Box> { .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_LIBCURL", "OFF") .define("LIBCZI_BUILD_AZURESDK_BASED_STREAM", "OFF") .define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_RAPIDJSON", "OFF") - .define("LIBCZI_BUILD_LIBCZIAPI", "ON") - .build(); + .define("LIBCZI_BUILD_LIBCZIAPI", "ON"); + + if is_cross && is_windows { + cmake_config + .define("CMAKE_SYSTEM_NAME", "Windows") + .define("CRASH_ON_UNALIGNED_ACCESS", "FALSE") + .define("ADDITIONAL_LIBS_REQUIRED_FOR_ATOMIC", ""); + + let has_toolchain_file = env::var("TARGET_CMAKE_TOOLCHAIN_FILE").is_ok() + || env::var(format!( + "CMAKE_TOOLCHAIN_FILE_{}", + target.replace('-', "_") + )) + .is_ok() + || env::var("CMAKE_TOOLCHAIN_FILE").is_ok(); + + if !has_toolchain_file { + cmake_config + .define("CMAKE_C_COMPILER", "clang") + .define("CMAKE_CXX_COMPILER", "clang++") + .define("CMAKE_RC_COMPILER", "llvm-rc"); + } + + fix_xwin_lib_case()?; + } + + if is_cross && is_windows { + cmake_config.build_target("libCZIAPIStatic"); + } + + let dst = cmake_config.build(); #[cfg(not(feature = "dynamic"))] let bindings = { - let mut libcziapi_a = dst.join("build/Src/libCZIAPI/liblibCZIAPIStatic.a"); - if !libcziapi_a.exists() { - libcziapi_a = dst.join("build/Src/libCZIAPI/liblibCZIAPIStatic.lib"); - } - bindgen::Builder::default().parse_callbacks(Box::new(DeMangler::new(libcziapi_a)?)) + let libcziapi_search_dirs = [ + dst.join("build/Src/libCZIAPI"), + dst.join("Src/libCZIAPI"), + dst.join("lib"), + dst.join("lib64"), + ]; + let libcziapi_static = libcziapi_search_dirs + .iter() + .find_map(|dir| find_static_lib(dir, "libCZIAPIStatic", is_windows)) + .ok_or(Error::msg("cannot find libCZIAPIStatic library"))?; + bindgen::Builder::default().parse_callbacks(Box::new(DeMangler::new( + libcziapi_static, + is_cross && is_windows, + )?)) }; #[cfg(feature = "dynamic")] @@ -101,22 +217,57 @@ fn main() -> Result<(), Box> { #[cfg(not(feature = "dynamic"))] { - println!( - "cargo::rustc-link-search=native={}", - dst.join("build/Src/libCZIAPI").display() - ); - println!("cargo::rustc-link-lib=static=libCZIAPIStatic"); + let libcziapi_dir = dst.join("build/Src/libCZIAPI"); + let libcziapi_dir2 = dst.join("Src/libCZIAPI"); + let libczi_dir = dst.join("build/Src/libCZI"); + let libczi_dir2 = dst.join("Src/libCZI"); + + let libcziapi_lib = find_static_lib(&libcziapi_dir, "libCZIAPIStatic", is_windows) + .or_else(|| find_static_lib(&libcziapi_dir2, "libCZIAPIStatic", is_windows)) + .ok_or(Error::msg("cannot find libCZIAPIStatic for linking"))?; + let libcziapi_name = lib_name_from_path(&libcziapi_lib)?; - println!( - "cargo::rustc-link-search=native={}", - dst.join("build/Src/libCZI").display() - ); let profile = env::var("PROFILE")?; - match profile.as_str() { - "debug" => println!("cargo::rustc-link-lib=static=libCZIStaticd"), - "release" => println!("cargo::rustc-link-lib=static=libCZIStatic"), + let libczi_base = match profile.as_str() { + "debug" => "libCZIStaticd", + "release" => "libCZIStatic", _ => return Err(Error::msg(format!("unsupported profile: {}", profile))), + }; + let libczi_lib = find_static_lib(&libczi_dir, libczi_base, is_windows) + .or_else(|| find_static_lib(&libczi_dir2, libczi_base, is_windows)) + .ok_or(Error::msg(format!( + "cannot find {} for linking", + libczi_base + )))?; + let libczi_name = lib_name_from_path(&libczi_lib)?; + + if libcziapi_dir.exists() { + println!( + "cargo::rustc-link-search=native={}", + libcziapi_dir.display() + ); } + if libcziapi_dir2.exists() { + println!( + "cargo::rustc-link-search=native={}", + libcziapi_dir2.display() + ); + } + println!("cargo::rustc-link-lib=static={}", libcziapi_name); + + if libczi_dir.exists() { + println!( + "cargo::rustc-link-search=native={}", + libczi_dir.display() + ); + } + if libczi_dir2.exists() { + println!( + "cargo::rustc-link-search=native={}", + libczi_dir2.display() + ); + } + println!("cargo::rustc-link-lib=static={}", libczi_name); println!( "cargo::rustc-link-search=native={}", @@ -126,7 +277,40 @@ fn main() -> Result<(), Box> { "cargo::rustc-link-search=native={}", dst.join("lib64").display() ); - println!("cargo::rustc-link-lib=static=zstd"); + if is_windows && is_cross { + let zstd_dir = dst.join("build/_deps/zstd-build/lib"); + let zstd_dir2 = dst.join("_deps/zstd-build/lib"); + if zstd_dir.exists() { + println!( + "cargo::rustc-link-search=native={}", + zstd_dir.display() + ); + } + if zstd_dir2.exists() { + println!( + "cargo::rustc-link-search=native={}", + zstd_dir2.display() + ); + } + let zstd_name = find_static_lib(&zstd_dir, "zstd_static", is_windows) + .or_else(|| find_static_lib(&zstd_dir2, "zstd_static", is_windows)) + .map(|p| lib_name_from_path(&p).unwrap_or_else(|_| "zstd_static".to_string())) + .unwrap_or_else(|| "zstd_static".to_string()); + println!("cargo::rustc-link-lib=static={}", zstd_name); + } else { + println!("cargo::rustc-link-lib=static=zstd"); + } + + if is_windows { + println!("cargo::rustc-link-lib=kernel32"); + println!("cargo::rustc-link-lib=advapi32"); + println!("cargo::rustc-link-lib=bcrypt"); + println!("cargo::rustc-link-lib=ntdll"); + println!("cargo::rustc-link-lib=user32"); + println!("cargo::rustc-link-lib=ole32"); + println!("cargo::rustc-link-lib=shell32"); + println!("cargo::rustc-link-lib=Windowscodecs"); + } } #[cfg(feature = "dynamic")] @@ -150,12 +334,18 @@ struct DeMangler { #[cfg(not(feature = "dynamic"))] impl DeMangler { - fn new(a_file: PathBuf) -> Result> { - let cmd = std::process::Command::new("nm").arg(&a_file).output()?; - let pat = Regex::new(r"^[\da-f]*\s[A-Z]\s(.*_Z(\d+)(libCZI_.*))$")?; + fn new(a_file: PathBuf, use_llvm_nm: bool) -> Result> { + let nm_cmd = if use_llvm_nm { "llvm-nm" } else { "nm" }; + let cmd = std::process::Command::new(nm_cmd) + .arg("--defined-only") + .arg(&a_file) + .output()?; + let itanium_pat = Regex::new(r"^[\da-f]*\s[A-Z]\s(.*_Z(\d+)(libCZI_.*))$")?; + let msvc_pat = Regex::new(r"^[\da-f]*\s[TDB]\s(\?(libCZI_\w+)@@.*)$")?; let mut map = HashMap::new(); for line in std::str::from_utf8(&cmd.stdout)?.lines() { - if let Some(caps) = pat.captures(line.trim()) { + let line = line.trim(); + if let Some(caps) = itanium_pat.captures(line) { if let (Some(symbol), Some(n), Some(name)) = (caps.get(1), caps.get(2), caps.get(3)) { let n: usize = n.as_str().parse()?; @@ -176,6 +366,24 @@ impl DeMangler { map.insert(demangled, mangled); } } + } else if let Some(caps) = msvc_pat.captures(line) { + let mangled = caps.get(1).unwrap().as_str().to_string(); + if let Some(name_match) = caps.get(2) { + let demangled = name_match.as_str().to_string(); + if let Some(existing_mangled) = map.get(&demangled) { + if existing_mangled != &mangled { + return Err(Error::msg(format!( + "conflicting mangled symbols for {} in {}: {}, {}", + demangled, + a_file.to_string_lossy(), + existing_mangled, + mangled + ))); + } + } else { + map.insert(demangled, mangled); + } + } } } Ok(Self { map }) diff --git a/src/functions.rs b/src/functions.rs index 0066f43..d1e7122 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -3,10 +3,15 @@ use crate::handle::*; use crate::interop::*; use crate::misc::*; use crate::sys::*; -use std::ffi::{CStr, CString, c_char, c_int, c_ulong, c_void}; +use std::ffi::{CStr, CString, c_char, c_int, c_void}; use std::mem::{ManuallyDrop, MaybeUninit}; use std::ops::Deref; +#[cfg(not(target_os = "windows"))] +type WChar = u32; +#[cfg(target_os = "windows")] +type WChar = u16; + /// Release the memory - this function is to be used for freeing memory allocated by the libCZIApi-library /// (and returned to the caller). /// @@ -25,7 +30,7 @@ pub fn free(data: T) { pub fn allocate_memory(size: usize) -> Result, Error> { let mut data = MaybeUninit::::uninit(); let mut ptr = data.as_mut_ptr() as *mut c_void; - lib_czi_error(unsafe { libCZI_AllocateMemory(size as c_ulong, &mut ptr) })?; + lib_czi_error(unsafe { libCZI_AllocateMemory(size as u64, &mut ptr) })?; Ok(data) } @@ -356,7 +361,7 @@ impl InputStream { /// \\param \[out\] stream_object The output stream object that will hold the created stream. /// \\return An error-code that indicates whether the operation is successful or not. Non-positive values indicates successful, positive values /// indicates unsuccessful operation. - pub fn create_from_file_wide(file_name: Vec) -> Result { + pub fn create_from_file_wide(file_name: Vec) -> Result { let mut stream = MaybeUninit::uninit(); let ptr = stream.as_mut_ptr(); lib_czi_error(unsafe { libCZI_CreateInputStreamFromFileWide(file_name.as_ptr(), ptr) })?; @@ -464,7 +469,7 @@ impl SubBlock { /// \\returns An error-code indicating success or failure of the operation. pub fn get_raw_data(&self, tp: RawDataType, size: i32) -> Result<(i32, Vec), Error> { let mut data = vec![0u8; size as usize]; - let size = Box::into_raw(Box::new(size as c_ulong)); + let size = Box::into_raw(Box::new(size as u64)); lib_czi_error(unsafe { libCZI_SubBlockGetRawData(**self, tp as c_int, size, data.as_mut_ptr() as *mut c_void) })?; @@ -511,7 +516,7 @@ impl Attachment { /// \\returns An error-code indicating success or failure of the operation. pub fn get_raw_data(&self, size: i32) -> Result<(i32, Vec), Error> { let mut data = vec![0u8; size as usize]; - let size = Box::into_raw(Box::new(size as c_ulong)); + let size = Box::into_raw(Box::new(size as u64)); lib_czi_error(unsafe { libCZI_AttachmentGetRawData(**self, size, data.as_mut_ptr() as *mut c_void) })?; @@ -827,7 +832,7 @@ impl OutputStream { /// /// \\return An error-code that indicates whether the operation is successful or not. Non-positive values indicates successful, positive values /// indicates unsuccessful operation. - pub fn create_for_file_wide(file_name: Vec, overwrite: bool) -> Result { + pub fn create_for_file_wide(file_name: Vec, overwrite: bool) -> Result { let mut output_stream = MaybeUninit::uninit(); let ptr = output_stream.as_mut_ptr(); lib_czi_error(unsafe {