1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
use std::{env, fs, path::{Path, PathBuf}};
struct ProbedLib {
version: String,
include_paths: Vec<PathBuf>,
}
/// Finds libxml2 and optionally return a list of header
/// files from which the bindings can be generated.
fn find_libxml2() -> Option<ProbedLib> {
#![allow(unreachable_code)] // for platform-dependent dead code
if let Ok(ref s) = std::env::var("LIBXML2") {
// println!("{:?}", std::env::vars());
// panic!("set libxml2.");
let p = std::path::Path::new(s);
let fname = std::path::Path::new(
p.file_name()
.unwrap_or_else(|| panic!("no file name in LIBXML2 env ({s})")),
);
assert!(
p.is_file(),
"{}",
&format!("not a file in LIBXML2 env ({s})")
);
println!(
"cargo:rustc-link-lib={}",
fname
.file_stem()
.unwrap()
.to_string_lossy()
.strip_prefix("lib")
.unwrap()
);
println!(
"cargo:rustc-link-search={}",
p.parent()
.expect("no library path in LIBXML2 env")
.to_string_lossy()
);
None
} else {
#[cfg(any(target_family = "unix", target_os = "macos", all(target_family="windows", target_env="gnu")))]
{
let lib = pkg_config::Config::new()
.probe("libxml-2.0")
.expect("Couldn't find libxml2 via pkg-config");
return Some(ProbedLib {
include_paths: lib.include_paths,
version: lib.version,
})
}
#[cfg(all(target_family = "windows", target_env = "msvc"))]
{
if let Some(meta) = vcpkg_dep::vcpkg_find_libxml2() {
return Some(meta);
} else {
eprintln!("vcpkg did not succeed in finding libxml2.");
}
}
panic!("Could not find libxml2.")
}
}
fn generate_bindings(header_dirs: Vec<PathBuf>, output_path: &Path) {
let bindings = bindgen::Builder::default()
.header("src/wrapper.h")
.opaque_type("max_align_t")
// invalidate build as soon as the wrapper changes
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.layout_tests(true)
.clang_args(&["-DPKG-CONFIG"])
.clang_args(
header_dirs.iter()
.map(|dir| format!("-I{}", dir.display()))
);
bindings
.generate()
.expect("failed to generate bindings with bindgen")
.write_to_file(output_path)
.expect("Failed to write bindings.rs");
}
fn main() {
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)");
if let Some(probed_lib) = find_libxml2() {
// if we could find header files, generate fresh bindings from them
generate_bindings(probed_lib.include_paths, &bindings_path);
// and expose the libxml2 version to the code
let version_parts: Vec<i32> = probed_lib.version.split('.')
.map(|part| part.parse::<i32>().unwrap_or(-1)).collect();
let older_than_2_12 = version_parts.len() > 1 && (version_parts[0] < 2 ||
version_parts[0] == 2 && version_parts[1] < 12);
println!("cargo::rustc-check-cfg=cfg(libxml_older_than_2_12)");
if older_than_2_12 {
println!("cargo::rustc-cfg=libxml_older_than_2_12");
}
} else {
// otherwise, use the default bindings on platforms where pkg-config isn't available
fs::copy(PathBuf::from("src/default_bindings.rs"), bindings_path)
.expect("Failed to copy the default bindings to the build directory");
// for now, assume that the library is older than 2.12, because that's what those bindings are computed with
println!("cargo::rustc-cfg=libxml_older_than_2_12");
}
}
#[cfg(all(target_family = "windows", target_env = "msvc"))]
mod vcpkg_dep {
use crate::ProbedLib;
pub fn vcpkg_find_libxml2() -> Option<ProbedLib> {
if let Ok(metadata) = vcpkg::Config::new()
.find_package("libxml2") {
Some(ProbedLib { version: vcpkg_version(), include_paths: metadata.include_paths })
} else {
None
}
}
fn vcpkg_version() -> String {
// What is the best way to obtain the version on Windows *before* bindgen runs?
// here we attempt asking the shell for "vcpkg list libxml2"
let mut vcpkg_exe = vcpkg::find_vcpkg_root(&vcpkg::Config::new()).unwrap();
vcpkg_exe.push("vcpkg.exe");
let vcpkg_list_libxml2 = std::process::Command::new(vcpkg_exe)
.args(["list","libxml2"])
.output()
.expect("vcpkg.exe failed to execute in vcpkg_dep build step");
if vcpkg_list_libxml2.status.success() {
let libxml2_list_str = String::from_utf8_lossy(&vcpkg_list_libxml2.stdout);
for line in libxml2_list_str.lines() {
if line.starts_with("libxml2:") {
let mut version_piece = line.split("2.");
version_piece.next();
if let Some(version_tail) = version_piece.next() {
if let Some(version) = version_tail.split(' ').next()
.unwrap().split('#').next() {
return format!("2.{version}");
}
}
}
}
}
// default to a recent libxml2 from Windows 10
// (or should this panic?)
String::from("2.13.5")
}
}
|