Skip to content
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
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
# Change Log

## [0.3.13] (in development)
## [0.3.13] (2026-06-11)

### Added

* Opt-in **static linking** of libxml2. Set the `LIBXML2_STATIC` environment
variable and `build.rs` probes via pkg-config with `.statik(true)`,
emitting `cargo:rustc-link-lib=static=xml2` (plus the transitive `-lm`).
Point `PKG_CONFIG_PATH` at a non-system prefix holding a PIC `libxml2.a`
to get a binary with no runtime `libxml2.so` dependency — independent of
the host's libxml2 SONAME, which bumped `.so.2` → `.so.16` at libxml2 2.14.
Unset (the default) is unchanged: a normal dynamic link against the host
libxml2.
* New cargo features **`runtime`** (default) and **`static`** that select how
bindgen locates libclang to generate the FFI bindings at build time.
`cargo build --no-default-features --features static` routes bindgen to
`clang-sys/static`, enabling fully static / musl / Alpine builds where a
runtime `dlopen` of libclang is unavailable. Together with `LIBXML2_STATIC`,
this resolves [#110](https://github.com/KWARC/rust-libxml/issues/110).

## [0.3.12] (2026-05-23)

Expand Down
20 changes: 16 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libxml"
version = "0.3.12"
version = "0.3.13"
edition = "2024"
authors = ["Andreas Franzén <andreas@devil.se>", "Deyan Ginev <deyan.ginev@gmail.com>","Jan Frederik Schaefer <j.schaefer@jacobs-university.de>"]
description = "A Rust wrapper for libxml2 - the XML C parser and toolkit developed for the Gnome project"
Expand All @@ -17,6 +17,21 @@ exclude = [
[lib]
name = "libxml"

[features]
# How bindgen locates libclang to generate the FFI bindings at BUILD time.
# Orthogonal to the `LIBXML2_STATIC` env var, which controls how libxml2 itself is
# linked into your binary (see build.rs). Together they let you build a fully
# static / musl / Alpine binary — see issue #110.
default = ["runtime"]
# clang-sys loads libclang dynamically (dlopen) at build time — the usual desktop
# setup; needs a libclang shared library available while building.
runtime = ["bindgen/runtime"]
# clang-sys links libclang statically — required for fully static / musl / Alpine
# build environments where dlopen of libclang is unavailable. Select it WITHOUT
# the default feature: cargo build --no-default-features --features static
# (clang-sys then needs a static libclang, e.g. via LLVM_CONFIG_PATH).
static = ["bindgen/static"]

[dependencies]
libc = "0.2"

Expand All @@ -34,9 +49,6 @@ pkg-config = "0.3.2"

[build-dependencies.bindgen]
version = "0.72.1"
features = [
"runtime",
]
default-features = false

[dev-dependencies]
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ So you have to install CLang 9.0 or greater:
- Debian / Ubuntu: `$ apt install libclang-dev`
- Fedora: `$ dnf install clang-devel`

### Static linking (musl / Alpine / fully static binaries)

Two independent knobs build a binary with no runtime libxml2 / libclang
dependency (see [#110](https://github.com/KWARC/rust-libxml/issues/110)):

* **`static` cargo feature** — selects how `bindgen` finds libclang at build
time. The default `runtime` feature `dlopen`s libclang, which is unavailable
in fully static / musl environments; `static` links it via `clang-sys/static`
instead:

```
cargo build --no-default-features --features static
```

* **`LIBXML2_STATIC` env var** — statically links libxml2 *itself*. Point
`PKG_CONFIG_PATH` at a prefix holding a PIC `libxml2.a` and set
`LIBXML2_STATIC=1`; `build.rs` then emits `cargo:rustc-link-lib=static=xml2`,
so the binary carries no `libxml2.so` SONAME dependency.

### MacOS
[Community contributed](https://github.com/KWARC/rust-libxml/issues/88#issuecomment-890876895):

Expand Down
15 changes: 14 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,18 @@ fn find_libxml2() -> Option<ProbedLib> {
all(target_family = "windows", target_env = "gnu")
))]
{
let lib = pkg_config::Config::new()
// Opt-in static link: when LIBXML2_STATIC is set and PKG_CONFIG_PATH points
// at a non-system prefix carrying a PIC `libxml2.a`, `.statik(true)` makes
// pkg-config emit `cargo:rustc-link-lib=static=xml2` plus the transitive
// `-lm` (which stays dynamic). pkg-config's static guard refuses a `static=`
// for a /usr/lib system path, so the non-system prefix is what enables this.
// Unset (the default) is unchanged: a normal dynamic link against the host
// libxml2. Used for the self-contained, SONAME-independent release binary.
let mut cfg = pkg_config::Config::new();
if std::env::var_os("LIBXML2_STATIC").is_some() {
cfg.statik(true);
}
let lib = cfg
.probe("libxml-2.0")
.expect("Couldn't find libxml2 via pkg-config");
return Some(ProbedLib {
Expand Down Expand Up @@ -92,6 +103,8 @@ fn generate_bindings(header_dirs: Vec<PathBuf>, output_path: &Path) {
}

fn main() {
// Re-run if the opt-in static toggle flips (see find_libxml2's static branch).
println!("cargo:rerun-if-env-changed=LIBXML2_STATIC");
let bindings_path = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("bindings.rs");
// declare availability of config variable (without setting it)
println!("cargo::rustc-check-cfg=cfg(libxml_older_than_2_12)");
Expand Down
Loading