mujoco-rs 0.3.0

A Rust wrapper around the MuJoCo C library, with a native simple viewer (re-)written in Rust.
use bindgen::callbacks::TypeKind;
use bindgen::callbacks::{ParseCallbacks, DeriveInfo};
use std::path::PathBuf;
use std::env;
use std::fs;


#[derive(Debug)]
struct CloneCallback;

impl ParseCallbacks for CloneCallback {
    fn add_derives(&self, info: &DeriveInfo) -> Vec<String> {
        let mut data = vec!["Clone".into()];
        if info.name.starts_with("mjui") || info.name == "mjrRect_" {
            data.push("Copy".into());
        }

        if info.kind == TypeKind::Enum {
            data.push("TryFromPrimitive".into());
        }

        data
    }
}



fn main() {
    /// Environmental variable which contains the path to the MuJoCo's build/lib/ directory.
    /// This mean for static linking, otherwise the dynamic MuJoCo library can just be installed and used.
    const MUJOCO_STATIC_LIB_PATH_VAR: &str = "MUJOCO_STATIC_LINK_LIB";
    const MUJOCO_DYN_LIB_PATH_VAR: &str = "MUJOCO_DYNAMIC_LINK_LIB";
    const GENERATE_FFI: &str = "GENERATE_FFI";

    println!("cargo:rerun-if-env-changed={MUJOCO_STATIC_LIB_PATH_VAR}");
    println!("cargo:rerun-if-env-changed={MUJOCO_DYN_LIB_PATH_VAR}");
    println!("cargo:rerun-if-env-changed={GENERATE_FFI}");

    let mujoco_lib_path= env::var(MUJOCO_STATIC_LIB_PATH_VAR);

    /* Static linking */
    if let Ok(path) = mujoco_lib_path {
        let mj_lib_pathbuf = PathBuf::from(path);
        let mj_lib_simulate_path = mj_lib_pathbuf.join("libsimulate.a");

        println!("cargo::rerun-if-changed={}", mj_lib_simulate_path.canonicalize().unwrap().display());
        println!("cargo:rustc-link-search={}", mj_lib_pathbuf.canonicalize().unwrap().display());

        #[cfg(feature = "cpp-viewer")]
        println!("cargo:rustc-link-lib=simulate");

        println!("cargo:rustc-link-lib=mujoco");
        println!("cargo:rustc-link-lib=lodepng");
        println!("cargo:rustc-link-lib=tinyxml2");
        println!("cargo:rustc-link-lib=qhullstatic_r");
        println!("cargo:rustc-link-lib=ccd");

        if cfg!(unix) {
            println!("cargo:rustc-link-lib=stdc++");
        }
    }

    /* Dynamic linking */
    else {
        let mujoco_dylib_path = PathBuf::from(env::var(MUJOCO_DYN_LIB_PATH_VAR)
            .unwrap_or_else(|_| panic!("nor the static library path ({MUJOCO_STATIC_LIB_PATH_VAR}),\
                nor the dynamic library path ({MUJOCO_DYN_LIB_PATH_VAR}) was given.")));

        println!("cargo:rustc-link-search={}", mujoco_dylib_path.canonicalize().unwrap().display());
        println!("cargo:rustc-link-lib=mujoco");
    }

    if let Ok(value) = env::var(GENERATE_FFI) {
        if value == "y" || value == "1" {
            let output_path = PathBuf::from("./src/");
            let current_dir = env::current_dir().unwrap();
            let include_dir_mujoco = current_dir.join("src/cpp/include/mujoco");
            let include_dir_simulate = include_dir_mujoco.join("viewer");

            // Generate MuJoCo bindings
            let bindings_mujoco = bindgen::Builder::default()
                .header(include_dir_mujoco.join("mujoco.h").to_str().unwrap())
                .header(include_dir_simulate.join("simulate.hpp").to_str().unwrap())
                .header(include_dir_simulate.join("glfw_dispatch.hpp").to_str().unwrap())
                .clang_arg("-std=c++20")
                .clang_arg("-stdlib=libc++")
                .clang_arg(format!("-I{}", current_dir.join("mujoco/build/_deps/glfw3-src/include/").display()))
                .blocklist_item("std::tuple.*")
                .allowlist_item("mj.*")
                .allowlist_item("mujoco::.*")
                .allowlist_item("new_simulate")
                .allowlist_item("free_simulate")
                .layout_tests(false)
                .derive_default(false)
                .opaque_type("std::.*")
                .derive_copy(false)
                .rustified_enum(".*")
                .parse_callbacks(Box::new(CloneCallback))
                .generate()
                .expect("unable to generate MuJoCo bindings");

            let outputfile_dir = output_path.join("mujoco_c.rs");
            let mut fdata = bindings_mujoco
                .to_string();

            // Extra adjustments
            fdata = fdata.replace("pub __lx: std_basic_string_value_type<_CharT>,", "pub __lx: std::mem::ManuallyDrop<std_basic_string_value_type<_CharT>>,");
            let re = regex::Regex::new(r"#\[derive\((.*?Clone.*?), Clone, (.*?)\)\]").unwrap();
            fdata = re.replace_all(&fdata, "#[derive($1, $2)]").to_string();
            fdata = "use num_enum::TryFromPrimitive;\n\n".to_string() + &fdata;

            fs::write(outputfile_dir, fdata).unwrap();
        }
    }
}