// Copyright 2021, pdfium-sys Developers // Copyright 2022 - 2024, pdfium-render Developers // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. #[cfg(feature = "bindings")] extern crate bindgen; #[cfg(feature = "bindings")] use { bindgen::BindgenError, std::env::var, std::ffi::OsStr, std::fs::{read_dir, write}, std::path::PathBuf, }; #[derive(Debug)] enum BuildError { #[cfg(feature = "bindings")] #[allow(dead_code)] IoError(std::io::Error), #[cfg(feature = "bindings")] #[allow(dead_code)] BindgenError(BindgenError), #[cfg(feature = "bindings")] #[allow(dead_code)] PathConversionError(PathBuf), } #[cfg(feature = "bindings")] impl From for BuildError { fn from(item: std::io::Error) -> Self { BuildError::IoError(item) } } #[cfg(feature = "bindings")] impl From for BuildError { fn from(item: BindgenError) -> Self { BuildError::BindgenError(item) } } fn main() -> Result<(), BuildError> { #[cfg(feature = "bindings")] build_bindings()?; #[cfg(feature = "static")] statically_link_pdfium(); Ok(()) } #[cfg(feature = "bindings")] fn build_bindings() -> Result<(), BuildError> { // AJRC - 3-Jan-22 - modified from the pdfium-sys version to removing explicit linking to // a system-provided pdfium. We still want the bindings generated by rust-bindgen, // since they provide various constants that are useful, but we will load functions // dynamically at runtime using libloading. // AJRC - 13-Jan-22 - docs.rs runs cargo doc in a read-only sandbox, so we can't // generate bindings. Skip bindings generation entirely if the DOCS_RS environment // variable is set, as per https://docs.rs/about/builds#detecting-docsrs. // AJRC - 22-Jan-22 - expanded bindings generation to cover all Pdfium modules, not // just the viewing and rendering functionality defined in fpdfview.h. // AJRC - 5-Aug-24 - expanded bindings generation to cover multiple versions of the Pdfium API. // Consumers can use crate feature flags to select a specific API version to use. for release in read_dir("include/")? { let release = release?.path(); if release.is_dir() { build_bindings_for_one_pdfium_release( release .file_name() .ok_or(BuildError::PathConversionError(release.clone()))? .to_str() .ok_or(BuildError::PathConversionError(release.clone()))?, )?; } } Ok(()) } #[cfg(feature = "bindings")] fn build_bindings_for_one_pdfium_release(release: &str) -> Result<(), BuildError> { if var("DOCS_RS").is_err() { // The DOCS_RS environment variable is _not_ set. Proceed with bindgen. // Generate an import wrapper telling bindgen which header files to use as input sources. let header_file_extension = OsStr::new("h"); let wrapper_file_name = OsStr::new("rust-import-wrapper.h"); let mut included_header_files = Vec::new(); for header in read_dir(format!("include/{}/", release))? { let header = header?.path(); if header.is_file() && header.file_name().is_some() && header.extension() == Some(header_file_extension) && header.file_name() != Some(wrapper_file_name) { // Include this header file in the list of input sources. let header_file_name = header .file_name() .ok_or(BuildError::PathConversionError(header.clone()))? .to_str() .ok_or(BuildError::PathConversionError(header.clone()))? .to_owned(); included_header_files.push(header_file_name); } } let wrapper = included_header_files .iter() .map(|file_name| format!("#include \"{}\"", file_name)) .collect::>() .join("\n"); write( format!("include/{}/rust-import-wrapper.h", release), wrapper, )?; let bindings = bindgen::Builder::default() // The input header we would like to generate bindings for. .header(format!("include/{}/rust-import-wrapper.h", release)) .clang_arg("-DPDF_USE_SKIA") // Also generate bindings for optional SKIA functions .clang_arg("-D_SKIA_SUPPORT_") // (Alternative name for this setting in Pdfium 5961 and earlier) .clang_arg("-DPDF_ENABLE_XFA") // Also generate bindings for optional XFA functions .clang_arg("-DPDF_ENABLE_V8") // Also generate bindings for optional V8 functions .generate_cstr(true) // Recommended for Rust 1.59 and later .enable_function_attribute_detection() .size_t_is_usize(true) // There are esoteric architectures where size_t != usize. See: // https://github.com/rust-lang/rust-bindgen/issues/1671 // Long term, the solution is for Bindgen to switch to converting size_t to // std::os::raw::c_size_t instead of usize, but c_size_t is not yet stabilized. .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) // Tell cargo to invalidate // the built crate whenever any of the included header files change. .clang_args( // Try to keep original C++ comments for docs. [ "-fretain-comments-from-system-headers", "-fparse-all-comments", ] .iter(), ) .generate_comments(true); #[cfg(feature = "pdfium_use_win32")] let bindings = bindings.clang_arg("-D_WIN32"); // Also generate bindings for Windows-specific functions // Generate the bindings to src/bindgen.rs. let bindings = bindings.generate()?; let out_path = PathBuf::from("src"); bindings.write_to_file(out_path.join(format!("bindgen/{}.rs", release)))?; } Ok(()) } #[cfg(feature = "static")] fn statically_link_pdfium() { if let Ok(path) = std::env::var("PDFIUM_STATIC_LIB_PATH") { // Instruct cargo to statically link the given library during the build. println!("cargo:rustc-link-lib=static=pdfium"); println!("cargo:rustc-link-search=native={}", path); // Optionally instruct cargo to link to a C++ standard library during the build. // TODO: AJRC - 30-Sep-22 - for now, we dynamically link to the selected standard library, // but ultimately we want to use a link type of "static:-bundle" once the feature is stabilized // (currently it is available only in nightly builds of Rust). #[cfg(feature = "libstdc++")] println!("cargo:rustc-link-lib=dylib=stdc++"); #[cfg(feature = "libc++")] println!("cargo:rustc-link-lib=dylib=c++"); } else if let Ok(path) = std::env::var("PDFIUM_DYNAMIC_LIB_PATH") { // Instruct cargo to dynamically link the given library during the build. println!("cargo:rustc-link-lib=dylib=pdfium"); println!("cargo:rustc-link-search=native={}", path); } }