Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Add optional support for building against the system library using pkg-config. #8

Merged
merged 5 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
shell: bash
- run: ci/set_rust_version.bash ${{ matrix.channel }} ${{ matrix.target }}
shell: bash
- run: ci/build.bash cargo ${{ matrix.target }}
- run: ci/build.bash cargo ${{ matrix.target }} vendor
shell: bash
- run: ci/test.bash cargo ${{ matrix.target }}
- run: ci/test.bash cargo ${{ matrix.target }} vendor
shell: bash

strategy:
Expand All @@ -30,8 +30,8 @@ jobs:
- uses: actions/checkout@v2
- run: git submodule update --init
- run: ci/set_rust_version.bash ${{ matrix.channel }} ${{ matrix.target }}
- run: ci/build.bash cargo ${{ matrix.target }}
- run: ci/test.bash cargo ${{ matrix.target }}
- run: ci/build.bash cargo ${{ matrix.target }} vendor
- run: ci/test.bash cargo ${{ matrix.target }} vendor

strategy:
fail-fast: false
Expand All @@ -45,6 +45,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: git submodule update --init
- run: sudo apt-get update && sudo apt-get install -yqq libqpdf-dev
- run: ci/set_rust_version.bash ${{ matrix.channel }} ${{ matrix.target }}
- run: ci/build.bash cargo ${{ matrix.target }}
- run: ci/test.bash cargo ${{ matrix.target }}
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ Tested on the following targets:

The prebuilt bindings for those targets are included in the source tree.

By default, `pkg-config` will be used to link against the system library `libqpdf`.

If the `vendored` feature is enabled, a vendored source tree of qpdf is built and linked statically.

The `legacy` feature enables bindings to the r2/3/4 encryption options which are available in qpdf 10.x but not 11.x.

## Usage example

```rust,no_run
Expand Down
14 changes: 10 additions & 4 deletions ci/build.bash
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@ source ci/common.bash

# $1 {path} = Path to cross/cargo executable
CROSS=$1
# $1 {string} = <Target Triple>
# $2 {string} = <Target Triple>
TARGET_TRIPLE=$2
# $3 {boolean} = Whether or not building for release or not.
RELEASE_BUILD=$3
# $3 {boolean} = Whether to use vendored sources.
VENDOR=$3
# $4 {boolean} = Whether or not building for release or not.
RELEASE_BUILD=$4

required_arg $CROSS 'CROSS'
required_arg $TARGET_TRIPLE '<Target Triple>'

if [ -n "$VENDOR" ]; then
VENDOR="--features vendored"
fi

if [ -z "$RELEASE_BUILD" ]; then
$CROSS build --target $TARGET_TRIPLE --workspace
$CROSS build --target $TARGET_TRIPLE $VENDOR --workspace
$CROSS build --target $TARGET_TRIPLE --all-features --workspace
else
$CROSS build --target $TARGET_TRIPLE --all-features --release --workspace
Expand Down
10 changes: 8 additions & 2 deletions ci/test.bash
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ source ci/common.bash

# $1 {path} = Path to cross/cargo executable
CROSS=$1
# $1 {string} = <Target Triple>
# $2 {string} = <Target Triple>
TARGET_TRIPLE=$2
# $3 {boolean} = Whether to use vendored sources.
VENDOR=$3

required_arg $CROSS 'CROSS'
required_arg $TARGET_TRIPLE '<Target Triple>'

$CROSS test --target $TARGET_TRIPLE --workspace
if [ -n "$VENDOR" ]; then
VENDOR="--features vendored"
fi

$CROSS test --target $TARGET_TRIPLE $VENDOR --workspace
$CROSS build --target $TARGET_TRIPLE --all-features --workspace
4 changes: 4 additions & 0 deletions qpdf-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ license.workspace = true
[dependencies]
qpdf-sys = { path = "../qpdf-sys", version = "0.1" }
libc = "0.2"

[features]
vendored = ["qpdf-sys/vendored"]
legacy = []
6 changes: 6 additions & 0 deletions qpdf-rs/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,13 @@ pub struct EncryptionParamsR6 {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum EncryptionParams {
/// R2 level, any PDF version
#[cfg(feature = "legacy")]
R2(EncryptionParamsR2),
/// R3 level, PDF version >= 1.4
#[cfg(feature = "legacy")]
R3(EncryptionParamsR3),
/// R4 level, PDF version >= 1.5
#[cfg(feature = "legacy")]
R4(EncryptionParamsR4),
/// R6 level, PDF version >= 1.7
R6(EncryptionParamsR6),
Expand Down Expand Up @@ -195,6 +198,7 @@ impl QPdfWriter {

fn set_encryption_params(&self, params: &EncryptionParams) -> Result<()> {
match params {
#[cfg(feature = "legacy")]
EncryptionParams::R2(r2) => {
let user_password = CString::new(r2.user_password.as_str())?;
let owner_password = CString::new(r2.owner_password.as_str())?;
Expand All @@ -212,6 +216,7 @@ impl QPdfWriter {
})?;
}
}
#[cfg(feature = "legacy")]
EncryptionParams::R3(r3) => {
let user_password = CString::new(r3.user_password.as_str())?;
let owner_password = CString::new(r3.owner_password.as_str())?;
Expand All @@ -232,6 +237,7 @@ impl QPdfWriter {
})?;
}
}
#[cfg(feature = "legacy")]
EncryptionParams::R4(r4) => {
let user_password = CString::new(r4.user_password.as_str())?;
let owner_password = CString::new(r4.owner_password.as_str())?;
Expand Down
8 changes: 5 additions & 3 deletions qpdf-rs/tests/test_qpdf.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::HashSet;

use qpdf::scalar::QPdfScalar;
use qpdf::*;

fn load_pdf() -> QPdf {
Expand All @@ -14,8 +13,8 @@ fn load_pdf_from_memory() -> QPdf {

#[test]
fn test_qpdf_version() {
assert_eq!(QPdf::library_version(), "10.6.3");
println!("{}", QPdf::library_version());
let version = dbg!(QPdf::library_version());
assert!(!version.is_empty());
}

#[test]
Expand Down Expand Up @@ -294,6 +293,7 @@ fn test_pdf_encrypted_write(params: EncryptionParams) {
assert!(qpdf.is_err());
}

#[cfg(feature = "legacy")]
#[test]
fn test_pdf_encrypted_r2_write() {
test_pdf_encrypted_write(EncryptionParams::R2(EncryptionParamsR2 {
Expand All @@ -306,6 +306,7 @@ fn test_pdf_encrypted_r2_write() {
}))
}

#[cfg(feature = "legacy")]
#[test]
fn test_pdf_encrypted_r3_write() {
test_pdf_encrypted_write(EncryptionParams::R3(EncryptionParamsR3 {
Expand All @@ -321,6 +322,7 @@ fn test_pdf_encrypted_r3_write() {
}))
}

#[cfg(feature = "legacy")]
#[test]
fn test_pdf_encrypted_r4_write() {
test_pdf_encrypted_write(EncryptionParams::R4(EncryptionParamsR4 {
Expand Down
6 changes: 5 additions & 1 deletion qpdf-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@ exclude = [
]

[build-dependencies]
cc = { version = "1", features = ["parallel"] }
cc = { version = "1", features = ["parallel"], optional = true }
bindgen = "0.69"
pkg-config = "0.3"

[features]
vendored = ["dep:cc"]
44 changes: 44 additions & 0 deletions qpdf-sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{env, path::PathBuf};

#[cfg(feature = "vendored")]
const ZLIB_SRC: &[&str] = &[
"adler32.c",
"compress.c",
Expand All @@ -14,6 +15,7 @@ const ZLIB_SRC: &[&str] = &[
"zutil.c",
];

#[cfg(feature = "vendored")]
const JPEG_SRC: &[&str] = &[
"jaricom.c",
"jcapimin.c",
Expand Down Expand Up @@ -63,6 +65,7 @@ const JPEG_SRC: &[&str] = &[
"jutils.c",
];

#[cfg(feature = "vendored")]
const QPDF_SRC: &[&str] = &[
"AES_PDF_native.cc",
"BitStream.cc",
Expand Down Expand Up @@ -155,6 +158,7 @@ const QPDF_SRC: &[&str] = &[
"SparseOHArray.cc",
];

#[cfg(feature = "vendored")]
fn base_build() -> cc::Build {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let mut build = cc::Build::new();
Expand All @@ -167,10 +171,12 @@ fn base_build() -> cc::Build {
build
}

#[cfg(feature = "vendored")]
fn is_msvc() -> bool {
env::var("TARGET").unwrap().ends_with("-msvc")
}

#[cfg(feature = "vendored")]
fn build_cc(name: &str, dir: &str, files: &[&str]) {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let path = root.join(dir);
Expand All @@ -188,6 +194,7 @@ fn build_cc(name: &str, dir: &str, files: &[&str]) {
.compile(name);
}

#[cfg(feature = "vendored")]
fn build_qpdf() {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let cpp_flags: &[&str] = if is_msvc() {
Expand Down Expand Up @@ -218,6 +225,7 @@ fn build_qpdf() {
build_cc("sha2", "qpdf/libqpdf", &["sha2.c", "sha2big.c"]);
}

#[cfg(feature = "vendored")]
fn build_bindings() {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
Expand All @@ -240,9 +248,45 @@ fn build_bindings() {
}
}

#[cfg(feature = "vendored")]
fn main() {
build_bindings();
build_cc("zlib", "zlib-1.2.11", ZLIB_SRC);
build_cc("jpeg", "jpeg-9d", JPEG_SRC);
build_qpdf();
}

#[cfg(not(feature = "vendored"))]
fn main() {
let lib = pkg_config::Config::new()
.atleast_version("10.6.3")
.probe("libqpdf")
.unwrap();

let mut builder = bindgen::builder();

for path in lib.include_paths {
builder = builder.clang_arg(format!("-I{}", path.to_str().unwrap()));

let header_path = path.join("qpdf/qpdf-c.h");
if header_path.exists() {
builder = builder.header(header_path.into_os_string().into_string().unwrap());
}
}

for (key, val) in lib.defines {
builder = builder.clang_arg(match val {
Some(val) => format!("-D{}={}", key, val),
None => format!("-D{}", key),
});
}

let bindings = builder
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.unwrap();

let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");

bindings.write_to_file(&out_path).unwrap();
}
Loading