jni 0.9.2

Rust bindings to the JNI
extern crate walkdir;

use std::env;
use std::fs::symlink_metadata;
use std::path::{
    Path,
    PathBuf,
};

fn main() {
    if cfg!(feature = "invocation") {
        let libjvm_path = env::var("JAVA_HOME")
            .ok()
            .and_then(|p| find_libjvm(p))
            .or_else(|| find_java_home().and_then(|p| find_libjvm(p)));

        match libjvm_path {
            Some(path) => println!("cargo:rustc-link-search=native={}", path.display()),
            None => panic!("Failed to find libjvm.so. Try setting JAVA_HOME"),
        }

        println!("cargo:rustc-link-lib=dylib=jvm");
    }
}

fn find_libjvm<S: AsRef<Path>>(path: S) -> Option<PathBuf> {
    let walker = walkdir::WalkDir::new(path).follow_links(true);

    let expected_name = if cfg!(target_os = "windows") {
        "jvm.dll"
    } else if cfg!(target_os = "linux") {
        "libjvm.so"
    } else {
        "libjvm.dylib"
    };

    for entry in walker {
        let entry = match entry {
            Ok(entry) => entry,
            Err(_e) => continue,
        };

        let file_name = entry.file_name().to_str().unwrap_or("");

        if file_name == expected_name {
            return entry.path().parent().map(Into::into);
        }
    }

    None
}

fn find_java_home() -> Option<PathBuf> {
    let path = match env::var("PATH").ok() {
        Some(p) => p,
        None => return None,
    };

    let path_sep = if cfg!(target_os = "windows") {
        ";"
    } else {
        ":"
    };

    let paths = path.split(path_sep);
    let (mut exe_path, mut exe_meta): (PathBuf, _) = paths
        .filter_map(|p| symlink_metadata(p).map(|m| (p.into(), m)).ok())
        .nth(0)?;

    while exe_meta.file_type().is_symlink() {
        exe_path = ::std::fs::read_link(&exe_path).ok()?;
        exe_meta = symlink_metadata(&exe_path).ok()?;
    }

    exe_path.parent().and_then(|p| p.parent()).map(Into::into)
}