The eigen/ directory was a byte-identical copy of upstream libeigen/eigen tag 3.4.0 (commit 3147391d, the same release libCZI pins in cmake/ExternalEIGEN3.cmake). Replace the copy with a shallow git submodule (.gitmodules: shallow = true) for verifiable provenance and smaller fork. The Eigen3::Eigen interface config moves to eigen-cfg/Eigen3Config.cmake (the Eigen repo only ships the Eigen3Config.cmake.in template), and build.rs points Eigen3_DIR there.
352 lines
14 KiB
Rust
352 lines
14 KiB
Rust
extern crate bindgen;
|
|
|
|
use std::env;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use std::fmt::Debug;
|
|
|
|
use std::fmt::{Display, Formatter};
|
|
|
|
fn find_static_lib(dir: &Path, base_name: &str, target_is_windows: bool) -> Option<PathBuf> {
|
|
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,
|
|
target_is_windows: bool,
|
|
) -> Result<String, Box<dyn std::error::Error>> {
|
|
let file_name = lib_path
|
|
.file_stem()
|
|
.ok_or(Error::msg("cannot get file stem"))?
|
|
.to_str()
|
|
.ok_or(Error::msg("cannot into string"))?;
|
|
// On Unix, `cargo::rustc-link-lib=static=NAME` makes the linker search for libNAME.a,
|
|
// so strip any leading "lib" prefix to avoid liblibNAME.a.
|
|
// On MSVC, the linker searches for NAME.lib directly, so keep the full name
|
|
// (e.g. libCZIAPIStatic.lib needs link-lib=static=libCZIAPIStatic).
|
|
if target_is_windows {
|
|
Ok(file_name.to_string())
|
|
} else {
|
|
Ok(file_name
|
|
.strip_prefix("lib")
|
|
.unwrap_or(file_name)
|
|
.to_string())
|
|
}
|
|
}
|
|
|
|
struct Error(String);
|
|
|
|
impl Debug for Error {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str(&self.0)
|
|
}
|
|
}
|
|
|
|
impl Display for Error {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str(&self.0)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
impl Error {
|
|
fn msg<S: ToString>(msg: S) -> Box<dyn std::error::Error> {
|
|
Box::new(Error(msg.to_string()))
|
|
}
|
|
}
|
|
|
|
fn fix_xwin_lib_case() -> Result<(), Box<dyn std::error::Error>> {
|
|
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 arch = env::var("TARGET")
|
|
.map(|t| t.split('-').next().unwrap_or_default().to_string())
|
|
.unwrap_or_default();
|
|
let xwin_sdk_lib = cargo_xwin_base.join("xwin/sdk/lib/um").join(arch);
|
|
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<dyn std::error::Error>> {
|
|
if env::var("DOCS_RS").is_ok() {
|
|
println!("cargo::rustc-cfg=docsrs");
|
|
} else {
|
|
let host = env::var("HOST")?;
|
|
let target = env::var("TARGET")?;
|
|
let is_cross = host != target
|
|
|| env::var(format!("CMAKE_TOOLCHAIN_FILE_{}", target.replace('-', "_"))).is_ok()
|
|
|| env::var("TARGET_CMAKE_TOOLCHAIN_FILE").is_ok()
|
|
|| env::var("CMAKE_TOOLCHAIN_FILE").is_ok();
|
|
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 manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?);
|
|
let mut cmake_config = cmake::Config::new(&libczi_dir);
|
|
cmake_config
|
|
.cxxflag("-fms-extensions")
|
|
.cxxflag("-DLIBCZIAPI_EXPORTS_SHARED")
|
|
.define("ADDITIONAL_LIBS_REQUIRED_FOR_ATOMIC", "")
|
|
.define("LIBCZI_BUILD_UNITTESTS", "OFF")
|
|
.define("LIBCZI_BUILD_CZICMD", "OFF")
|
|
.define("LIBCZI_BUILD_DYNLIB", "OFF")
|
|
.define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_ZSTD", "OFF")
|
|
.define("LIBCZI_BUILD_CURL_BASED_STREAM", "OFF")
|
|
.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")
|
|
// Eigen 3.4.0 comes from the shallow git submodule (eigen/, pinned
|
|
// at the official tag 3.4.0) so neither a network clone (libCZI's
|
|
// gitlab ExternalProject) nor a system libeigen3-dev package is
|
|
// required; Eigen3_DIR finds the small interface config in
|
|
// eigen-cfg/Eigen3Config.cmake
|
|
.define("LIBCZI_BUILD_PREFER_EXTERNALPACKAGE_EIGEN3", "ON")
|
|
.define("Eigen3_DIR", manifest_dir.join("eigen-cfg"));
|
|
|
|
if is_cross {
|
|
if is_windows {
|
|
cmake_config
|
|
.define("CMAKE_SYSTEM_NAME", "Windows")
|
|
.define("CRASH_ON_UNALIGNED_ACCESS", "FALSE")
|
|
// the rest of the cross build (Rust std, cc-rs-compiled
|
|
// vendored code) uses the dynamic C runtime (/MD), but
|
|
// libCZI hardwires the static one (/MT); the MSVC linker
|
|
// (lld-link) rejects the mix. Use libCZI's upstream option
|
|
// to leave the runtime choice to CMake, aligned with
|
|
// the dynamic runtime (only affects the msvc target)
|
|
.define("LIBCZI_DO_NOT_SET_MSVC_RUNTIME_LIBRARY", "ON")
|
|
.define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreadedDLL$<$<CONFIG:Debug>:Debug>");
|
|
|
|
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()?;
|
|
} else if target.contains("apple") {
|
|
cmake_config
|
|
.define("CMAKE_SYSTEM_NAME", "Darwin")
|
|
.define("CRASH_ON_UNALIGNED_ACCESS", "OFF");
|
|
if target.contains("aarch64") {
|
|
cmake_config.define("NEON_INTRINSICS_CAN_BE_USED", "ON");
|
|
}
|
|
} else {
|
|
cmake_config
|
|
.define("CRASH_ON_UNALIGNED_ACCESS", "OFF")
|
|
.define("_UNALIGNED_ACCESS_RESULT_EXITCODE", "0");
|
|
if target.contains("aarch64") {
|
|
cmake_config.define("_NEON_INTRINSICS_RESULT_EXITCODE", "0");
|
|
}
|
|
}
|
|
}
|
|
|
|
if is_cross && is_windows {
|
|
cmake_config.build_target("libCZIAPIStatic");
|
|
}
|
|
|
|
let dst = cmake_config.build();
|
|
|
|
let bindings = bindgen::Builder::default()
|
|
.merge_extern_blocks(true)
|
|
.clang_args([
|
|
"-fms-extensions",
|
|
"-DLIBCZIAPI_EXPORTS_SHARED",
|
|
"-x",
|
|
"c++",
|
|
"-std=c++14",
|
|
"-I",
|
|
libcziapi_inc
|
|
.to_str()
|
|
.ok_or(Error::msg("cannot into string"))?,
|
|
"-I",
|
|
libcziapi_src
|
|
.to_str()
|
|
.ok_or(Error::msg("cannot into string"))?,
|
|
"-I",
|
|
libczi_src
|
|
.to_str()
|
|
.ok_or(Error::msg("cannot into string"))?,
|
|
])
|
|
// cargo-zigbuild puts zig's bundled libc++ on the include path
|
|
// (CPATH); its hardening.h requires the -D_LIBCPP_* config macros.
|
|
// Pass them ourselves so parsing works with any ziglang /
|
|
// cargo-zigbuild version (they are ignored by other stdlibs)
|
|
.clang_args([
|
|
"-D_LIBCPP_ASSERTION_SEMANTIC_DEFAULT=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE",
|
|
"-D_LIBCPP_PSTL_BACKEND_SERIAL",
|
|
"-D_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS=0",
|
|
"-D_LIBCPP_HAS_TERMINAL",
|
|
"-D_LIBCPP_HAS_RANDOM_DEVICE",
|
|
"-D_LIBCPP_HAS_NO_STD_MODULES",
|
|
"-D_LIBCPP_HAS_FILESYSTEM=1",
|
|
])
|
|
.header(
|
|
libcziapi_h
|
|
.to_str()
|
|
.ok_or(Error::msg("cannot into string"))?,
|
|
)
|
|
.generate()?;
|
|
bindings.write_to_file(dst.join("lib_czi_api.rs"))?;
|
|
|
|
if cfg!(any(not(feature = "dynamic"), target_feature = "crt-static")) {
|
|
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, is_windows)?;
|
|
|
|
let profile = env::var("PROFILE")?;
|
|
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, is_windows)?;
|
|
|
|
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={}",
|
|
dst.join("lib").display()
|
|
);
|
|
println!(
|
|
"cargo::rustc-link-search=native={}",
|
|
dst.join("lib64").display()
|
|
);
|
|
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, is_windows)
|
|
.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");
|
|
}
|
|
} else {
|
|
println!(
|
|
"cargo::rustc-link-search=native={}",
|
|
dst.join("build/Src/libCZIAPI").display()
|
|
);
|
|
println!("cargo::rustc-link-lib=libCZIAPI");
|
|
}
|
|
}
|
|
println!("cargo::rerun-if-changed=build.rs");
|
|
println!("cargo::rerun-if-changed=libczi");
|
|
Ok(())
|
|
}
|