From cbcf98342c8f492f67a7591856b3a4a670290a78 Mon Sep 17 00:00:00 2001 From: oracola91 <118899664+oracola91@users.noreply.github.com> Date: Fri, 16 Oct 2026 05:25:39 +0300 Subject: [PATCH] perf: make build deps for `pdfium` feature on-demand (#14) --- Cargo.toml | 22 +++-- build.rs | 278 +++++++++++++++++++++++++++-------------------------- 2 files changed, 157 insertions(+), 143 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9257ce..7ff2009 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,13 +53,14 @@ features = [ "Win32_UI_WindowsAndMessaging", ] +# Only the `pdfium` feature's build script uses these, to download and verify PDFium. [build-dependencies] -reqwest = { version = "0.13", features = ["blocking"] } -flate2 = "1.1" -tar = "0.4" -sigstore-trust-root = { version = "0.6.5", default-features = false } -sigstore-types = "0.6.5" -sigstore-verify = { version = "0.6.5", default-features = false } +reqwest = { version = "0.13", features = ["blocking"], optional = true } +flate2 = { version = "1.1", optional = true } +tar = { version = "0.4", optional = true } +sigstore-trust-root = { version = "0.6.5", default-features = false, optional = true } +sigstore-types = { version = "0.6.5", optional = true } +sigstore-verify = { version = "0.6.5", default-features = false, optional = true } [dev-dependencies] ctor = "0.10" @@ -74,7 +75,14 @@ tiff = "0.11" [features] default = ["pdfium"] -pdfium = [] +pdfium = [ + "dep:reqwest", + "dep:flate2", + "dep:tar", + "dep:sigstore-trust-root", + "dep:sigstore-types", + "dep:sigstore-verify", +] test-utils = ["dep:sha2", "dep:bs58", "dep:uuid"] [package.metadata.docs.rs] diff --git a/build.rs b/build.rs index 0a2001e..7f72880 100644 --- a/build.rs +++ b/build.rs @@ -1,156 +1,162 @@ -use std::{ - borrow::Cow, - env, - error::Error, - ffi::OsStr, - fs, - io::Cursor, - path::{Path, PathBuf}, -}; +use std::env; -use flate2::read::GzDecoder; -use reqwest::blocking::Response; -use sigstore_trust_root::{TrustedRoot, SIGSTORE_PRODUCTION_TRUSTED_ROOT}; -use sigstore_types::Bundle; -use sigstore_verify::{verify, VerificationPolicy}; -use tar::{Archive, EntryType}; +// Downloading, verifying and linking PDFium. Only built with the `pdfium` feature, so its +// build-dependencies are not compiled without it. +#[cfg(feature = "pdfium")] +mod pdfium { + use std::{ + borrow::Cow, + env, + error::Error, + ffi::OsStr, + fs, + io::Cursor, + path::{Path, PathBuf}, + }; -const DEFAULT_PDFIUM_BUILD_ID: &str = "7802"; -const PDFIUM_ATTESTATION_NAME: &str = "pdfium-attestation.json"; -const PDFIUM_CERTIFICATE_ISSUER: &str = "https://token.actions.githubusercontent.com"; -const VERIFIED_MARKER_FILE: &str = ".sigstore-verified"; + use flate2::read::GzDecoder; + use reqwest::blocking::Response; + use sigstore_trust_root::{TrustedRoot, SIGSTORE_PRODUCTION_TRUSTED_ROOT}; + use sigstore_types::Bundle; + use sigstore_verify::{verify, VerificationPolicy}; + use tar::{Archive, EntryType}; -#[allow(dead_code)] -fn try_link_pdfium() -> Result<(), Box> { - let bin_ext = [ - OsStr::new("dll"), - OsStr::new("lib"), - OsStr::new("so"), - OsStr::new("a"), - ]; - let build_id = env::var("WINPRINT_PDFIUM_BUILD_ID") - .unwrap_or_else(|_| DEFAULT_PDFIUM_BUILD_ID.to_string()); - let mut target_path = PathBuf::from(env::var("OUT_DIR")?); - target_path.push(format!("pdfium_binaries_{}", build_id)); - fs::create_dir_all(target_path.as_path())?; + const DEFAULT_PDFIUM_BUILD_ID: &str = "7802"; + const PDFIUM_ATTESTATION_NAME: &str = "pdfium-attestation.json"; + const PDFIUM_CERTIFICATE_ISSUER: &str = "https://token.actions.githubusercontent.com"; + const VERIFIED_MARKER_FILE: &str = ".sigstore-verified"; - let has_bin = fs::read_dir(target_path.as_path())?.any(|x| { - bin_ext.iter().any(|s| { - x.as_ref() - .ok() - .and_then(|d| { - if Path::new(d.file_name().as_os_str()).extension() == Some(s) { - Some(()) - } else { - None - } - }) - .is_some() - }) - }); - let verified_marker = target_path.join(VERIFIED_MARKER_FILE); - - if !has_bin || !verified_marker.exists() { - let platform_name = match ( - env::var("CARGO_CFG_TARGET_OS")?.as_str(), - env::var("CARGO_CFG_TARGET_ARCH")?.as_str(), - ) { - ("windows", "x86") => "win-x86", - ("windows", "x86_64") => "win-x64", - ("windows", "aarch64") => "win-arm64", - _ => return Err("Unsupported target arch".into()), - }; - - let binary_package_name = format!("pdfium-{}.tgz", platform_name); - let binary_package_url = release_asset_url(&build_id, &binary_package_name); - let binary_package = download_asset(binary_package_url.as_str())?; - - let attestation_url = release_asset_url(&build_id, PDFIUM_ATTESTATION_NAME); - let attestation = download_asset(attestation_url.as_str())?; - - verify_pdfium_attestation(binary_package.as_slice(), attestation.as_slice())?; - - fs::remove_dir_all(target_path.as_path())?; + pub fn try_link_pdfium() -> Result<(), Box> { + let bin_ext = [ + OsStr::new("dll"), + OsStr::new("lib"), + OsStr::new("so"), + OsStr::new("a"), + ]; + let build_id = env::var("WINPRINT_PDFIUM_BUILD_ID") + .unwrap_or_else(|_| DEFAULT_PDFIUM_BUILD_ID.to_string()); + let mut target_path = PathBuf::from(env::var("OUT_DIR")?); + target_path.push(format!("pdfium_binaries_{}", build_id)); fs::create_dir_all(target_path.as_path())?; - unpack_pdfium_archive(binary_package.as_slice(), target_path.as_path(), &bin_ext)?; - fs::write( - verified_marker, - format!( - "Verified {} with {}\n", - binary_package_name, PDFIUM_ATTESTATION_NAME - ), - )?; + + let has_bin = fs::read_dir(target_path.as_path())?.any(|x| { + bin_ext.iter().any(|s| { + x.as_ref() + .ok() + .and_then(|d| { + if Path::new(d.file_name().as_os_str()).extension() == Some(s) { + Some(()) + } else { + None + } + }) + .is_some() + }) + }); + let verified_marker = target_path.join(VERIFIED_MARKER_FILE); + + if !has_bin || !verified_marker.exists() { + let platform_name = match ( + env::var("CARGO_CFG_TARGET_OS")?.as_str(), + env::var("CARGO_CFG_TARGET_ARCH")?.as_str(), + ) { + ("windows", "x86") => "win-x86", + ("windows", "x86_64") => "win-x64", + ("windows", "aarch64") => "win-arm64", + _ => return Err("Unsupported target arch".into()), + }; + + let binary_package_name = format!("pdfium-{}.tgz", platform_name); + let binary_package_url = release_asset_url(&build_id, &binary_package_name); + let binary_package = download_asset(binary_package_url.as_str())?; + + let attestation_url = release_asset_url(&build_id, PDFIUM_ATTESTATION_NAME); + let attestation = download_asset(attestation_url.as_str())?; + + verify_pdfium_attestation(binary_package.as_slice(), attestation.as_slice())?; + + fs::remove_dir_all(target_path.as_path())?; + fs::create_dir_all(target_path.as_path())?; + unpack_pdfium_archive(binary_package.as_slice(), target_path.as_path(), &bin_ext)?; + fs::write( + verified_marker, + format!( + "Verified {} with {}\n", + binary_package_name, PDFIUM_ATTESTATION_NAME + ), + )?; + } + + println!( + "cargo:rustc-link-search=native={}", + target_path.to_str().unwrap() + ); + println!("cargo:rustc-link-lib=dylib=pdfium"); + Ok(()) } - println!( - "cargo:rustc-link-search=native={}", - target_path.to_str().unwrap() - ); - println!("cargo:rustc-link-lib=dylib=pdfium"); - Ok(()) -} - -fn release_asset_url(build_id: &str, file_name: &str) -> String { - format!( - "https://github.com/bblanchon/pdfium-binaries/releases/download/chromium/{}/{}", - build_id, file_name - ) -} - -fn download_asset(url: &str) -> Result, Box> { - let resp = reqwest::blocking::get(url)?; - ensure_success(resp, url) -} - -fn ensure_success(resp: Response, url: &str) -> Result, Box> { - if !resp.status().is_success() { - return Err(format!("Failed to download asset from {}", url).into()); + fn release_asset_url(build_id: &str, file_name: &str) -> String { + format!( + "https://github.com/bblanchon/pdfium-binaries/releases/download/chromium/{}/{}", + build_id, file_name + ) } - Ok(resp.bytes()?.to_vec()) -} -fn verify_pdfium_attestation( - binary_package: &[u8], - attestation_json: &[u8], -) -> Result<(), Box> { - let bundle = Bundle::from_json(std::str::from_utf8(attestation_json)?)?; - let trusted_root = TrustedRoot::from_json(SIGSTORE_PRODUCTION_TRUSTED_ROOT)?; - let policy = VerificationPolicy::default().require_issuer(PDFIUM_CERTIFICATE_ISSUER); + fn download_asset(url: &str) -> Result, Box> { + let resp = reqwest::blocking::get(url)?; + ensure_success(resp, url) + } - verify(binary_package, &bundle, &policy, &trusted_root) - .map_err(|err| format!("Failed to verify PDFium Sigstore attestation: {err}"))?; + fn ensure_success(resp: Response, url: &str) -> Result, Box> { + if !resp.status().is_success() { + return Err(format!("Failed to download asset from {}", url).into()); + } + Ok(resp.bytes()?.to_vec()) + } - Ok(()) -} + fn verify_pdfium_attestation( + binary_package: &[u8], + attestation_json: &[u8], + ) -> Result<(), Box> { + let bundle = Bundle::from_json(std::str::from_utf8(attestation_json)?)?; + let trusted_root = TrustedRoot::from_json(SIGSTORE_PRODUCTION_TRUSTED_ROOT)?; + let policy = VerificationPolicy::default().require_issuer(PDFIUM_CERTIFICATE_ISSUER); -fn unpack_pdfium_archive( - archive_bytes: &[u8], - target_path: &Path, - bin_ext: &[&OsStr], -) -> Result<(), Box> { - let tar = GzDecoder::new(Cursor::new(archive_bytes)); - let mut archive = Archive::new(tar); + verify(binary_package, &bundle, &policy, &trusted_root) + .map_err(|err| format!("Failed to verify PDFium Sigstore attestation: {err}"))?; - for entry in archive.entries()? { - let mut file = entry?; - if file.header().entry_type() == EntryType::Regular { - let file_path = file.path()?; - let file_extension = file_path.extension(); - let is_bin = bin_ext.iter().any(|s| file_extension == Some(s)); - if is_bin { - if let Some(file_name_raw) = file_path.file_name() { - let mut file_name_str = file_name_raw.to_string_lossy(); - if let Some(s) = file_name_str.strip_suffix(".dll.lib") { - file_name_str = Cow::Owned(s.to_owned() + ".lib"); + Ok(()) + } + + fn unpack_pdfium_archive( + archive_bytes: &[u8], + target_path: &Path, + bin_ext: &[&OsStr], + ) -> Result<(), Box> { + let tar = GzDecoder::new(Cursor::new(archive_bytes)); + let mut archive = Archive::new(tar); + + for entry in archive.entries()? { + let mut file = entry?; + if file.header().entry_type() == EntryType::Regular { + let file_path = file.path()?; + let file_extension = file_path.extension(); + let is_bin = bin_ext.iter().any(|s| file_extension == Some(s)); + if is_bin { + if let Some(file_name_raw) = file_path.file_name() { + let mut file_name_str = file_name_raw.to_string_lossy(); + if let Some(s) = file_name_str.strip_suffix(".dll.lib") { + file_name_str = Cow::Owned(s.to_owned() + ".lib"); + } + let output_path = target_path.join(file_name_str.as_ref()); + file.unpack(output_path.as_path())?; } - let output_path = target_path.join(file_name_str.as_ref()); - file.unpack(output_path.as_path())?; } } } - } - Ok(()) + Ok(()) + } } fn main() { @@ -162,6 +168,6 @@ fn main() { let target_windows = env::var("CARGO_CFG_TARGET_OS").unwrap(); if target_windows == "windows" { #[cfg(feature = "pdfium")] - try_link_pdfium().unwrap(); + pdfium::try_link_pdfium().unwrap(); } }