use std::env;
use std::path::PathBuf;
use std::process::Command;
use glob::glob;
use lazy_static::lazy_static;
pub mod errors;
const WINDOWS: &'static str = "windows";
const MACOS: &'static str = "macos";
const ANDROID: &'static str = "android";
const UNIX: &'static str = "unix";
lazy_static! {
static ref TARGET_OS: String = {
let target_os_res = env::var("CARGO_CFG_TARGET_OS");
let tos = target_os_res.as_ref().map(|x| &**x).unwrap_or_else(|_| {
if cfg!(windows) {
WINDOWS
} else if cfg!(target_os = "macos") {
MACOS
} else if cfg!(target_os = "android") {
ANDROID
} else {
UNIX
}
});
tos.to_string()
};
}
fn is_windows() -> bool {
&*TARGET_OS == WINDOWS
}
fn is_macos() -> bool {
&*TARGET_OS == MACOS
}
#[allow(dead_code)]
fn is_android() -> bool {
&*TARGET_OS == ANDROID
}
#[allow(dead_code)]
fn is_unix() -> bool {
&*TARGET_OS == UNIX
}
pub fn get_jvm_dyn_lib_file_name() -> &'static str {
if is_windows() {
"jvm.dll"
} else if is_macos() {
"libjvm.dylib"
} else {
"libjvm.so"
}
}
pub fn locate_java_home() -> errors::Result<String> {
match &env::var("JAVA_HOME") {
Ok(s) if s.is_empty() => {
do_locate_java_home()
}
Ok(java_home_env_var) => Ok(java_home_env_var.clone()),
Err(_) => {
do_locate_java_home()
}
}
}
fn do_locate_java_home() -> errors::Result<String> {
let command_str = if is_windows() {
"where"
} else if is_macos() {
"/usr/libexec/java_home"
} else {
"which"
};
let mut command = Command::new(command_str);
if !is_macos() {
command.arg("java");
}
let output = command.output().map_err(|error| {
let message = format!("Command '{}' is not found in the system PATH ({})", command_str, error);
errors::JavaLocatorError::new(&message)
})?;
let java_exec_path = String::from_utf8(output.stdout)
.map(|jp| {
let mut lines: Vec<&str> = jp.lines().collect();
if lines.len() > 1 {
println!("WARNING: java_locator found {} possible java locations: {}. Using the last one.",
lines.len(),
lines.join(", "));
lines.remove(lines.len() - 1).to_string()
} else {
jp
}
})?;
if java_exec_path.is_empty() {
Err(errors::JavaLocatorError::new("Java is not installed or not added in the system PATH"))?
}
let mut test_path = PathBuf::from(java_exec_path.trim());
while let Ok(path) = test_path.read_link() {
test_path = if path.is_absolute() {
path
} else {
test_path.pop();
test_path.push(path);
test_path
};
}
test_path.pop();
test_path.pop();
match test_path.to_str() {
Some(s) => Ok(String::from(s)),
None => Err(errors::JavaLocatorError::new(&format!("Could not convert path {:?} to String", test_path))),
}
}
pub fn locate_jvm_dyn_library() -> errors::Result<String> {
let jvm_dyn_lib_file_name = if is_windows() {
"jvm.dll"
} else {
"libjvm.*"
};
locate_file(jvm_dyn_lib_file_name)
}
pub fn locate_file(file_name: &str) -> errors::Result<String> {
let java_home = locate_java_home()?;
let query = format!("{}/**/{}", java_home, file_name);
let paths_vec: Vec<String> = glob(&query)?
.filter_map(Result::ok)
.map(|path_buf| {
let mut pb = path_buf.clone();
pb.pop();
pb.to_str().unwrap_or("").to_string()
})
.filter(|s: &String| !s.is_empty())
.collect();
if paths_vec.is_empty() {
Err(errors::JavaLocatorError::new(&format!("Could not find the {} library in any subdirectory of {}", file_name, java_home)))
} else {
Ok(paths_vec[0].clone())
}
}
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
fn locate_java_home_test() {
println!("locate_java_home: {}", locate_java_home().unwrap());
println!("locate_jvm_dyn_library: {}", locate_jvm_dyn_library().unwrap());
}
}