Skip to content

Commit a814737

Browse files
dginevclaude
andauthored
build: opt-in static libxml2 link via LIBXML2_STATIC (v0.3.13) (#202)
* build: opt-in static libxml2 link via LIBXML2_STATIC (v0.3.13) When LIBXML2_STATIC is set, build.rs probes via pkg-config with .statik(true), emitting static=xml2 (+ transitive -lm), for a SONAME-independent self-contained downstream binary. Default (env unset) is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: opt-in static bindgen/clang-sys via `static` cargo feature (#110) Adds runtime (default) / static cargo features selecting how bindgen finds libclang. `--no-default-features --features static` routes bindgen to clang-sys/static for fully static / musl / Alpine builds where dlopen of libclang is unavailable. Orthogonal to LIBXML2_STATIC (which links libxml2 itself); together they close #110. Default behavior unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent da0f3ef commit a814737

4 files changed

Lines changed: 67 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
# Change Log
22

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

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

Cargo.toml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "libxml"
3-
version = "0.3.12"
3+
version = "0.3.13"
44
edition = "2024"
55
authors = ["Andreas Franzén <andreas@devil.se>", "Deyan Ginev <deyan.ginev@gmail.com>","Jan Frederik Schaefer <j.schaefer@jacobs-university.de>"]
66
description = "A Rust wrapper for libxml2 - the XML C parser and toolkit developed for the Gnome project"
@@ -17,6 +17,21 @@ exclude = [
1717
[lib]
1818
name = "libxml"
1919

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

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

3550
[build-dependencies.bindgen]
3651
version = "0.72.1"
37-
features = [
38-
"runtime",
39-
]
4052
default-features = false
4153

4254
[dev-dependencies]

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ So you have to install CLang 9.0 or greater:
3434
- Debian / Ubuntu: `$ apt install libclang-dev`
3535
- Fedora: `$ dnf install clang-devel`
3636

37+
### Static linking (musl / Alpine / fully static binaries)
38+
39+
Two independent knobs build a binary with no runtime libxml2 / libclang
40+
dependency (see [#110](https://github.com/KWARC/rust-libxml/issues/110)):
41+
42+
* **`static` cargo feature** — selects how `bindgen` finds libclang at build
43+
time. The default `runtime` feature `dlopen`s libclang, which is unavailable
44+
in fully static / musl environments; `static` links it via `clang-sys/static`
45+
instead:
46+
47+
```
48+
cargo build --no-default-features --features static
49+
```
50+
51+
* **`LIBXML2_STATIC` env var** — statically links libxml2 *itself*. Point
52+
`PKG_CONFIG_PATH` at a prefix holding a PIC `libxml2.a` and set
53+
`LIBXML2_STATIC=1`; `build.rs` then emits `cargo:rustc-link-lib=static=xml2`,
54+
so the binary carries no `libxml2.so` SONAME dependency.
55+
3756
### MacOS
3857
[Community contributed](https://github.com/KWARC/rust-libxml/issues/88#issuecomment-890876895):
3958

build.rs

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

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

0 commit comments

Comments
 (0)