minutus 0.1.1-alpha.4

mruby bridge for Rust
use anyhow::{anyhow, Result};
use std::env;
use std::path::Path;

use minutus_mruby_build_utils::MRubyBuilder;

fn main() -> Result<()> {
    println!("cargo:rerun-if-changed=src/bridge");
    println!("cargo:rerun-if-changed=build.rs");

    let out_dir_str = env::var("OUT_DIR")?;
    let base_dir = Path::new(&out_dir_str);

    let builder = MRubyBuilder {
        base_dir: base_dir,
        mruby_version: mruby_version(),
    };

    builder.download_mruby()?;
    compile_bridge(&base_dir)?;

    if env::var("CARGO_FEATURE_LINK_MRUBY").is_ok() {
        builder.link_mruby()?
    }

    println!("Finish build.rs");

    Ok(())
}

fn mruby_version() -> String {
    let default = "3.1.0";
    let supported_versions = &["3.1.0", "2.1.2", "MASTER"];
    for version in supported_versions.into_iter() {
        if env::var(format!(
            "CARGO_FEATURE_MRUBY_{}",
            str::replace(version, ".", "_")
        ))
        .is_ok()
        {
            return version.to_lowercase().to_string();
        }
    }
    return default.to_string();
}

fn compile_bridge(out_dir: &Path) -> Result<()> {
    // generate bridge.c
    let output = std::process::Command::new("ruby")
        .args(&["all.rb"])
        .current_dir(Path::new("src").join("bridge"))
        .output()?;
    if !output.status.success() {
        eprintln!("{}", String::from_utf8(output.stderr)?);
        return Err(anyhow!("Failed to execute command"));
    }
    std::fs::write(Path::new("src").join("bridge.c"), output.stdout)?;

    // generate binding
    println!("Start generating binding");

    let mruby_include_path = Path::new(out_dir).join("mruby").join("include");
    println!("include path: {}", mruby_include_path.to_str().unwrap());

    let out_path = Path::new(out_dir).join("mruby.rs");
    let allowlist_types = &[
        "minu_.*",
        "RClass",
        "RObject",
        "RBasic",
        "RData",
        "RString",
        "RInteger",
        "RFloat",
        "RRational",
        "RComplex",
        "RArray",
        "RHash",
        "RRange",
        "RProc",
        "RException",
    ];
    let allowlist_functions = &["minu_.*", "mrb_raise", "mrb_get_args"];
    let bindings = bindgen::Builder::default()
        .clang_arg(format!("-I{}", mruby_include_path.to_str().unwrap()))
        .header(Path::new("src").join("bridge.c").to_string_lossy())
        .allowlist_type(allowlist_types.join("|"))
        .allowlist_function(allowlist_functions.join("|"))
        .layout_tests(false)
        .generate_comments(false)
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()?;
    bindings.write_to_file(out_path)?;

    println!("Finish generating binding");

    // Compile
    println!("Start compiling binding");

    cc::Build::new()
        .file(Path::new("src").join("bridge.c"))
        .include(mruby_include_path)
        .compile("minutus_bridge");

    println!("Finish compiling binding");

    Ok(())
}