use std::{env, path::Path};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rustc-env=BUILD_TIMESTAMP={}",
chrono::Local::now().format("%H:%M:%S%z %Y-%m-%d"));
let output = std::process::Command::new("git")
.args(["rev-parse", "--short", "HEAD"]).output()?;
println!("cargo:rustc-env=BUILD_GIT_HASH={}", String::from_utf8(output.stdout)?);
println!("cargo:rerun-if-changed={}", Path::new(".git").join("index").display());
#[allow(unused)] let path = std::path::PathBuf::from(env::var("OUT_DIR")?);
#[cfg(feature = "ftg")] binding_ftg(&path)?;
#[cfg(feature = "evg")] binding_evg(&path)?;
#[cfg(feature = "b2d")] binding_b2d(&path)?;
#[cfg(feature = "ovg")] binding_ovg(&path)?;
#[cfg(feature = "ugl")] binding_ugl(&path)?;
Ok(())
}
#[cfg(feature = "ftg")] fn binding_ftg(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
#[derive(Debug)] struct DoctestComment;
impl bindgen::callbacks::ParseCallbacks for DoctestComment {
fn process_comment(&self, comment: &str) -> Option<String> {
Some(format!("```c,ignore\n{comment}\n```")) }
}
let (ftg_dir, module) = (Path::new("3rdparty").join("ftg"), "ftgrays");
cc::Build::new().flag("-std=c17").flag("-pedantic").define("STANDALONE_", None)
.define("FALL_THROUGH", "((void)0)").file(ftg_dir.join("ftgrays.c"))
.files(glob::glob(&format!("{}/stroke/*.c", ftg_dir.display()))?
.filter_map(Result::ok)).file(ftg_dir.join("ftraster.c"))
.flag("-Wno-unused").flag("-Wno-implicit-fallthrough")
.opt_level(3).define("NDEBUG", None).compile(module);
bindgen::builder().header(ftg_dir.join("ftgrays.h").to_string_lossy())
.clang_args(["-DSTANDALONE_", "-DFT_BEGIN_HEADER=", "-DFT_END_HEADER=",
"-DFT_STATIC_BYTE_CAST(type,var)=(type)(unsigned char)(var)",
]).allowlist_item("FT_OUTLINE_.*|FT_RASTER_FLAG_.*|FT_CURVE_TAG.*")
.allowlist_var("ft_grays_raster").allowlist_type("FT_Outline|FT_Pixel_Mode")
.allowlist_var("ft_standard_raster").merge_extern_blocks(true)
.layout_tests(false).derive_copy(false).derive_debug(false)
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.parse_callbacks(Box::new(DoctestComment)).generate_comments(false) .generate()?.write_to_file(path.join(format!("{module}.rs")))?;
Ok(())
}
#[cfg(feature = "evg")] fn binding_evg(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let (evg_dir, module) = (Path::new("3rdparty").join("evg"), "gpac_evg");
#[allow(unused_mut)] let mut bgen = bindgen::builder();
let mut cc = cc::Build::new();
#[cfg(feature = "evg_fixed")] {
cc.define("GPAC_FIXED_POINT", None);
bgen = bgen.clang_arg("-DGPAC_FIXED_POINT");
}
cc.flag("-std=c17").flag("-Wno-pointer-sign").define("GPAC_DISABLE_LOG", None)
.flag("-Wno-unused-parameter").define("GPAC_DISABLE_THREADS", None)
.flag("-Wno-implicit-fallthrough").flag("-Wno-unused")
.files(glob::glob(&format!("{}/*.c",
evg_dir.display()))?.filter_map(Result::ok))
.include(&evg_dir).opt_level(3).define("NDEBUG", None).compile(module);
bgen.header(evg_dir.join("gpac").join("evg.h").to_string_lossy())
.clang_args(["-DGPAC_DISABLE_THREADS", &format!("-I{}", evg_dir.display()) ])
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.allowlist_function("gf_evg_s.*").allowlist_function("gf_path_.*")
.merge_extern_blocks(true).new_type_alias("Fixed") .layout_tests(false).derive_copy(false).derive_debug(false)
.generate()?.write_to_file(path.join(format!("{module}.rs")))?;
Ok(())
}
#[cfg(feature = "b2d")] fn binding_b2d(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let b2d_src = Path::new("3rdparty").join("blend2d").join("src");
let jit_src = Path::new("3rdparty").join("asmjit") .join("src");
#[allow(unused_mut)] let mut bgen = bindgen::builder();
let module = "blend2d";
let mut cc = cc::Build::new();
#[cfg(feature = "b2d_sfp")] { #[cfg(target_arch = "aarch64")] cc.flag("-mno-outline-atomics");
cc.define("BLEND2D_NO_DFP", None).flag("-fsingle-precision-constant")
.define("BL_BUILD_NO_TLS", None).flag("-Wno-uninitialized").compiler("g++-13");
bgen = bgen.clang_arg("-DBLEND2D_NO_DFP");
} blend2d_simd(&mut cc);
cc.cpp(true).flag("-std=c++17").define("ASMJIT_EMBED", None)
.define("ASMJIT_NO_STDCXX", None).define("ASMJIT_NO_FOREIGN", None)
.files(glob::glob(&format!("{}/**/*.cpp",
jit_src.display()))?.filter_map(Result::ok))
.files(glob::glob(&format!("{}/**/*.cpp", b2d_src.display()))?
.filter_map(|f| f.ok().filter(|f|
f.as_os_str().to_str().is_some_and(|f| !f.contains("_test")))))
.flag("-fvisibility=hidden").flag("-fno-exceptions").flag("-fno-math-errno")
.flag("-fmerge-all-constants").flag("-ftree-vectorize").flag("-fno-rtti")
.flag("-fno-threadsafe-statics").include(&b2d_src).include(jit_src)
.opt_level(3).define("NDEBUG", None).compile(module);
bgen.header(b2d_src.join("blend2d.h").to_string_lossy())
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.default_non_copy_union_style(bindgen::NonCopyUnionStyle::ManuallyDrop)
.default_visibility(bindgen::FieldVisibilityKind::PublicCrate)
.derive_copy(false).derive_debug(false).merge_extern_blocks(true)
.allowlist_function("bl.*").allowlist_type("BL.*").layout_tests(false)
.generate()?.write_to_file(path.join(format!("{module}.rs")))?;
fn blend2d_simd(cc: &mut cc::Build) {
if cfg!(target_arch = "arm") { let simd_flag = "-mfpu=neon-vfpv4";
cc.is_flag_supported(simd_flag).is_ok_and(|bl| bl)
.then(|| cc.flag(simd_flag).define("BL_BUILD_OPT_ASIMD", None));
}
let compiler = cc.get_compiler();
if compiler.is_like_gnu() { cc.flag_if_supported("-fno-semantic-interposition"); }
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
if compiler.is_like_msvc() { cc.flag("-MP").flag("-GR-").flag("-GF").flag("-Zc:__cplusplus").flag("-Zc:inline")
.flag("-Zc:strictStrings").flag("-Zc:threadSafeInit-").flag("-GS-").flag("-Oi");
let simd_flag = "-arch:AVX512";
cc.is_flag_supported(simd_flag).unwrap_or(false)
.then(|| cc.flag(simd_flag).define("BL_BUILD_OPT_AVX512", None));
let simd_flag = "-arch:AVX2";
if cc.is_flag_supported(simd_flag).unwrap_or(false) {
cc.flag(simd_flag).define("BL_BUILD_OPT_AVX2", None);
if compiler.is_like_clang() {
for simd_flag in ["-msse4.2", "-msse4.1", "-mssse3", "-msse3", "-msse2"] {
cc.is_flag_supported(simd_flag).unwrap_or(false).then(||
cc.flag(simd_flag).define("BL_BUILD_OPT_SSE2", None)); break;
}
cc.flag("-clang:-fno-rtti").flag("-clang:-fno-math-errno")
.flag("-clang:-fno-trapping-math");
} else {
cc.define("__SSSE3__", None).define("__SSE3__", None) .define("__SSE4_2__", None).define("__SSE4_1__", None);
}
#[cfg(target_arch = "x86_64")] cc.flag("-arch:SSE2");
}
let simd_flag = "-arch:AVX";
cc.is_flag_supported(simd_flag).unwrap_or(false)
.then(|| cc.flag(simd_flag).define("BL_BUILD_OPT_AVX", None));
} else {
is_x86_feature_detected!("avx512bw").then(||
cc.flag("-mavx512bw").define("BL_BUILD_OPT_AVX512", None));
is_x86_feature_detected!("avx512dq").then(||
cc.flag("-mavx512dq").define("BL_BUILD_OPT_AVX512", None));
is_x86_feature_detected!("avx512cd").then(||
cc.flag("-mavx512cd").define("BL_BUILD_OPT_AVX512", None));
is_x86_feature_detected!("avx512vl").then(||
cc.flag("-mavx512vl").define("BL_BUILD_OPT_AVX512", None));
is_x86_feature_detected!("avx2").then(|| {
cc.flag("-mavx2").define("BL_BUILD_OPT_AVX2", None);
cc.flag_if_supported("-mfma");
});
is_x86_feature_detected!("avx").then(||
cc.flag("-mavx").define("BL_BUILD_OPT_AVX", None));
is_x86_feature_detected!("sse4.2").then(||
cc.flag("-msse4.2").define("BL_BUILD_OPT_SSE4_2", None));
is_x86_feature_detected!("sse4.1").then(||
cc.flag("-msse4.1").define("BL_BUILD_OPT_SSE4_1", None));
is_x86_feature_detected!("ssse3").then(||
cc.flag("-mssse3").define("BL_BUILD_OPT_SSSE3", None));
is_x86_feature_detected!("sse3").then(||
cc.flag("-msse3").define("BL_BUILD_OPT_SSE3", None));
is_x86_feature_detected!("sse2").then(||
cc.flag("-msse2").define("BL_BUILD_OPT_SSE2", None));
}
}
Ok(())
}
#[cfg(feature = "ovg")] fn binding_ovg(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let mut ovg_dir = Path::new("3rdparty").join("amanithvg").join("include");
bindgen::builder().clang_arg(format!("-I{}", ovg_dir.display()))
.header(ovg_dir.join("VG").join("vgext.h").to_string_lossy())
.derive_copy(false).derive_debug(false).merge_extern_blocks(true)
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.allowlist_function("vg.*").allowlist_type("VG.*").layout_tests(false)
.generate()?.write_to_file(path.join("openvg.rs"))?;
ovg_dir.pop(); ovg_dir.push("lib"); ovg_dir.push(env::consts::OS); ovg_dir.push(env::consts::ARCH); ovg_dir.push("sre"); ovg_dir.push("standalone");
println!("cargo:rustc-link-search=native={}", ovg_dir.display());
println!("cargo:rustc-link-lib=dylib=AmanithVG");
Ok(())
}
#[cfg(feature = "ugl")] fn binding_ugl(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let ugl_inc = Path::new("3rdparty").join("micro-gl").join("include");
let module = "microgl";
let mut ugl_cpp = Path::new("src").join(module).with_extension("cpp");
cc::Build::new().cpp(true).flag("-std=c++17").file(&ugl_cpp)
.flag("-Wno-unused-parameter").flag("-Wno-unused").flag("-Wno-sign-compare")
.flag("-Wno-deprecated-copy").flag("-Wno-uninitialized")
.flag("-Wno-reorder").flag("-Wno-misleading-indentation")
.include(&ugl_inc).opt_level(3).define("NDEBUG", None).compile(module);
println!("cargo:rerun-if-changed={}", ugl_cpp.display());
ugl_cpp.set_extension("h");
bindgen::builder().header(ugl_cpp.to_string_lossy()).opaque_type("(canvas|path)_t")
.clang_args(["-x", "c++", "-std=c++17", &format!("-I{}", ugl_inc.display())])
.derive_copy(false).derive_debug(false).merge_extern_blocks(true)
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.allowlist_function("(canvas|path).*").layout_tests(false)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()?.write_to_file(path.join(format!("{module}.rs")))?;
Ok(())
}