ffms2-sys 0.3.0

FFI bindings to ffms2
extern crate bindgen;
extern crate metadeps;

use env::VarError;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn format_write(builder: bindgen::Builder) -> String {
    builder
        .generate()
        .unwrap()
        .to_string()
        .replace("/**", "/*")
        .replace("/*!", "/*")
}

fn main() {
    let use_static = env::var("CARGO_FEATURE_STATIC").is_ok();

    // Consider 'FFMS_INCLUDE_DIR' and 'FFMS_LIB_DIR', if pkg-config should not be used.
    let headers = env::var("FFMS_INCLUDE_DIR").map(|value| {
        // Ensure the include directory is valid
        let include_dir = PathBuf::from(value.as_str());
        if !include_dir.is_dir() {
            panic!("The specified include directory '{}' in FFMS_INCLUDE_DIR is not valid.", value);
        }

        // Get the path to the lib.
        let lib_dir = env::var("FFMS_LIB_DIR").and_then(|lib_dir| {
            match PathBuf::from(lib_dir) {
                lib_dir if lib_dir.is_dir() => Ok(lib_dir),
                _ => Err(VarError::NotPresent)
            }
        }).expect("FFMS_LIB_DIR is not set or the specified directory is not valid.");

        // Using dynamic library in Windows remains a problem. We have to copy the DLL into a path...
        // Problem: If 'FFMS_LIB_DIR' is outside of 'target', it is not considered (https://doc.rust-lang.org/cargo/reference/environment-variables.html).
        // Only handle DLL copying for dynamic linking on Windows
        #[cfg(windows)]
        if !use_static {
            let cargo_output_dir = env::var("OUT_DIR").expect("Unable to get OUT_DIR");
            let linkable_dll: PathBuf = [cargo_output_dir.as_ref(), "..", "..", "..", "ffms2.dll"].iter().collect();

            // Copy the file if it does not exists
            if !linkable_dll.is_file() {
                let dll_file = lib_dir.as_path().join("ffms2.dll");
                if !dll_file.is_file() {
                    panic!("Unable to find the 'ffms2.dll' in 'FFMS_LIB_DIR' ('{}').", lib_dir.display());
                }

                std::fs::copy(dll_file, linkable_dll).expect("Copying DLL failed");     
            }
        }

        // Add the flags for cargo otherwise explicitely added by pkg-config-rs
        if use_static {
            println!("cargo:rustc-link-lib=static=ffms2");
        } else {
            println!("cargo:rustc-link-lib=dylib=ffms2");
        }
        println!("cargo:rustc-link-search=native={}", lib_dir.to_string_lossy());

        vec![include_dir]
    }).unwrap_or_else(|_| {
        let libs = metadeps::probe().expect("Unable to query include paths using pkg-config. Consider setting the environment variable FFMS_INCLUDE_DIR and FFMS_LIB_DIR explicitely.");
        libs.get("ffms2").unwrap().include_paths.clone()
    });

    let mut builder = bindgen::builder().header("data/ffms.h");
    for header in headers {
        builder = builder.clang_arg("-I").clang_arg(header.to_str().unwrap());
    }

    builder = builder.default_enum_style(bindgen::EnumVariation::Rust {
        non_exhaustive: false,
    });

    // Manually fix the comment so rustdoc won't try to pick them
    let s = format_write(builder);

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

    let mut file = File::create(out_path.join("ffms2.rs")).unwrap();

    let _ = file.write(s.as_bytes());
}