diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..4b01400 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --manifest-path xtask/Cargo.toml --" diff --git a/.gitignore b/.gitignore index 96d4f30..25f0f3d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ # Generated by Cargo /target/ /libvlc-sys/target +/xtask/target Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 58c2317..9d39c33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ crate-type = ["rlib"] libc = "0.2" libvlc-sys = { path = "libvlc-sys" } -[features] -default = [] -use-bindgen = ["libvlc-sys/use-bindgen"] +# `xtask` contains maintenance tools; keep it out of the workspace so normal +# builds never compile it. +[workspace] +exclude = ["xtask"] diff --git a/libvlc-sys/Cargo.toml b/libvlc-sys/Cargo.toml index 51f88aa..b263112 100644 --- a/libvlc-sys/Cargo.toml +++ b/libvlc-sys/Cargo.toml @@ -22,12 +22,7 @@ crate-type = ["rlib"] libc = "0.2" [build-dependencies] -bindgen = { version = "0.72", optional = true } pkg-config = "0.3" [target.'cfg(target_os = "windows")'.build-dependencies] vswhom = "0.1.0" - -[features] -default = [] -use-bindgen = ["bindgen"] diff --git a/libvlc-sys/build.rs b/libvlc-sys/build.rs index 3405043..dd3eb63 100644 --- a/libvlc-sys/build.rs +++ b/libvlc-sys/build.rs @@ -1,54 +1,8 @@ -use std::env; -use std::path::PathBuf; -use pkg_config; +//! Build script for `libvlc-sys`, locates and links libvlc. /// Minimum supported libvlc version. const MIN_LIBVLC_VERSION: &str = "3.0.0"; -#[cfg(feature = "bindgen")] -fn generate_bindings(library: &pkg_config::Library) { - println!("cargo:rerun-if-changed=wrapper.h"); - - let mut bindings = bindgen::Builder::default() - .header("wrapper.h") - // For no_std - .use_core() - // Use libc - .ctypes_prefix("libc") - // Allowlist every (lib)vlc symbol. - .allowlist_item("(lib|LIB)?(vlc|VLC)_.*") - // Required by the Windows `legacy_stdio_definitions` link workaround - // (see `windows::link_vlc`). - .allowlist_function("vsnprintf") - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); - - // Set header include paths from the pkg-config probe. - for include_path in &library.include_paths { - bindings = bindings.clang_arg(format!("-I{}", include_path.display())); - } - - let bindings = bindings.generate().expect("Unable to generate bindings"); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); -} - -#[cfg(not(feature = "bindgen"))] -fn copy_pregenerated_bindings() -{ - use std::fs; - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - let crate_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - fs::copy( - crate_path.join("bindings.rs"), - out_path.join("bindings.rs"), - ) - .expect("Couldn't find pregenerated bindings!"); -} - /// Locate libvlc via pkg-config. fn probe_libvlc() -> Result { pkg_config::Config::new() @@ -142,8 +96,11 @@ mod windows { } ), ]) - .spawn() - .unwrap(); + .status() + .expect("Failed to run lib.exe") + .success() + .then_some(()) + .expect("lib.exe failed to generate the vlc import library"); } fn vlc_path() -> PathBuf { @@ -166,22 +123,9 @@ mod windows { } fn main() { - // Probe once and reuse the result for both binding generation (include - // paths) and linking (pkg-config emits the link directives on success). - let library = probe_libvlc(); - - // Binding generation - #[cfg(feature = "bindgen")] - generate_bindings(library.as_ref().expect( - "pkg-config is required to locate the libvlc headers with the `bindgen` feature", - )); - - #[cfg(not(feature = "bindgen"))] - copy_pregenerated_bindings(); - - // Link. On success pkg-config has already emitted the link directives; only - // the failure path needs handling. - if let Err(err) = library { + // On success pkg-config has already emitted the link directives; only the + // failure path needs handling. + if let Err(err) = probe_libvlc() { #[cfg(target_os = "windows")] windows::link_vlc(); diff --git a/libvlc-sys/src/lib.rs b/libvlc-sys/src/lib.rs index da4a48d..12b7d82 100644 --- a/libvlc-sys/src/lib.rs +++ b/libvlc-sys/src/lib.rs @@ -3,4 +3,6 @@ #![allow(non_snake_case)] #![no_std] -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +// The bindings are a committed source file, regenerated out of band with +// `cargo xtask bindgen`. +include!("../bindings.rs"); diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..a02dd56 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "xtask" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] +bindgen = "0.72" +pkg-config = "0.3" + +# Standalone from the vlc-rs workspace, kept out of the normal build graph. +[workspace] diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..6699e2b --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,70 @@ +//! Maintenance tasks for vlc-rs, invoked via `cargo xtask `. + +use std::path::{Path, PathBuf}; +use std::process::ExitCode; + +/// Minimum libvlc version the bindings target. Keep in sync with +/// `libvlc-sys/build.rs`. +const MIN_LIBVLC_VERSION: &str = "3.0.0"; + +fn main() -> ExitCode { + match std::env::args().nth(1).as_deref() { + Some("bindgen") => { + generate_bindings(); + ExitCode::SUCCESS + } + _ => { + eprintln!("usage: cargo xtask \n"); + eprintln!("tasks:"); + eprintln!(" bindgen regenerate libvlc-sys/bindings.rs from the system libvlc headers"); + ExitCode::FAILURE + } + } +} + +fn generate_bindings() { + let sys_dir = libvlc_sys_dir(); + let header = sys_dir.join("wrapper.h"); + let output = sys_dir.join("bindings.rs"); + + // Enforce the same minimum version the build script requires and pick up any + // header search paths. + let library = pkg_config::Config::new() + .atleast_version(MIN_LIBVLC_VERSION) + .cargo_metadata(false) + .probe("libvlc") + .unwrap_or_else(|e| panic!("libvlc >= {MIN_LIBVLC_VERSION} not found via pkg-config: {e}")); + + let mut bindings = bindgen::Builder::default() + .header(header.to_str().expect("non-UTF-8 header path")) + // For no_std + .use_core() + // Use libc + .ctypes_prefix("libc") + // Allowlist every (lib)vlc symbol. + .allowlist_item("(lib|LIB)?(vlc|VLC)_.*") + // Required by the Windows `legacy_stdio_definitions` link workaround + // (see libvlc-sys/build.rs). + .allowlist_function("vsnprintf"); + + for path in &library.include_paths { + bindings = bindings.clang_arg(format!("-I{}", path.display())); + } + + let generated = bindings + .generate() + .expect("unable to generate bindings") + .to_string(); + + std::fs::write(&output, generated).expect("couldn't write bindings"); + println!("wrote {}", output.display()); +} + +/// Absolute path to the `libvlc-sys` crate directory, derived from this xtask's +/// location so it works regardless of the current working directory. +fn libvlc_sys_dir() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("xtask has no parent directory") + .join("libvlc-sys") +}