fmod-audio-sys 2.220.2

ffi bindings to FMOD and FMOD Studio
Documentation
use std::path::PathBuf;

#[cfg(windows)]
fn find_fmod_directory() -> PathBuf {
    for drive in ["C", "D"] {
        let test_path = PathBuf::from(format!(
            "{drive}:/Program Files (x86)/FMOD SoundSystem/FMOD Studio API Windows"
        ));
        if test_path.exists() {
            return test_path;
        }
    }

    let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
    for path in [
        out_dir.join("FMOD Studio API Windows"),
        out_dir.join("FMOD SoundSystem"),
    ] {
        if path.exists() {
            return path;
        }
    }

    std::env::var_os("FMOD_SYS_FMOD_DIRECTORY")
        .map(PathBuf::from)
        .unwrap_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fmod"))
}

#[cfg(not(windows))]
fn find_fmod_directory() -> PathBuf {
    let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
    let out_path = out_dir.join("fmod");
    if out_path.exists() {
        return out_path;
    }

    std::env::var_os("FMOD_SYS_FMOD_DIRECTORY")
        .map(PathBuf::from)
        .unwrap_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fmod"))
}

fn main() {
    // skip generating bindings in docs.rs, as we use the packaged "documentation.rs" instead
    if std::env::var("DOCS_RS").is_ok() {
        return;
    }
    #[cfg(feature = "force-docs-bindings")]
    return;

    let fmod_dir = find_fmod_directory();
    let api_dir = fmod_dir.join("api");

    assert!(fmod_dir.exists(), "fmod directory not present");
    assert!(api_dir.exists(), "fmod api dir does not exist");

    let api_dir_display = api_dir.display();
    println!("cargo:rerun-if-changed=\"{api_dir_display}/core/inc\"");
    println!("cargo:rerun-if-changed=\"{api_dir_display}/studio/inc\"");

    let bindgen = bindgen::builder()
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .clang_arg(format!("-I{api_dir_display}/core/inc"))
        .clang_arg(format!("-I{api_dir_display}/studio/inc"))
        .newtype_enum("FMOD_RESULT")
        .must_use_type("FMOD_RESULT")
        .new_type_alias("FMOD_BOOL")
        .derive_partialeq(true)
        .derive_eq(true)
        .impl_partialeq(true)
        .derive_hash(true)
        .derive_default(true)
        .prepend_enum_name(false) // fmod already does this
        .header("src/wrapper.h");

    #[cfg(target_arch = "x86")]
    let target_arch = "x86";
    #[cfg(all(target_arch = "x86_64", not(windows)))]
    let target_arch = "x86_64";
    #[cfg(all(target_arch = "x86_64", windows))]
    let target_arch = "x64";

    let include_debug = cfg!(any(debug_assertions, feature = "force-debug"));
    let debug_char = if include_debug { "L" } else { "" };

    println!("cargo:rustc-link-search={api_dir_display}/core/lib/{target_arch}");
    println!("cargo:rustc-link-search={api_dir_display}/studio/lib/{target_arch}");

    #[cfg(target_os = "linux")]
    {
        println!("cargo:rustc-link-lib=fmod{debug_char}");
        println!("cargo:rustc-link-lib=fmodstudio{debug_char}");
    }
    #[cfg(target_os = "windows")]
    {
        println!("cargo:rustc-link-lib=fmod{debug_char}_vc");
        println!("cargo:rustc-link-lib=fmodstudio{debug_char}_vc");
    }

    let bindings = bindgen.generate().expect("failed to generate bindings");
    let out_path = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("bindings.rs");

    bindings
        .write_to_file(out_path)
        .expect("failed to write bindings");

    let docs_path = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap())
        .join("docs/documentation.rs");

    bindings
        .write_to_file(docs_path)
        .expect("failed to write bindings");

    println!("cargo:rerun-if-changed=\"src/channel_control.cpp\"");
    println!("cargo:rerun-if-changed=\"src/channel_control.h\"");

    // wrapper does not use the stdlib
    let mut build = cc::Build::new();

    build
        .cpp(true)
        .cpp_link_stdlib(None)
        .cpp_set_stdlib(None)
        .include(format!("{api_dir_display}/core/inc"))
        .file("src/channel_control.cpp");
    #[cfg(target_os = "windows")]
    {
        #[cfg(target_arch = "x86_64")]
        let target = "x86_64-pc-windows-msvc";
        #[cfg(target_arch = "x86")]
        let target = "i686-pc-windows-msvc";
        let tool = cc::windows_registry::find_tool(target, "cl.exe").expect("failed to find cl");
        build.compiler(tool.path());
    }

    build.compile("channel_control_wrapper");
}