1 //! This build script is used to link with `jvm` dynamic library when
2 //! `invocation` feature is enabled.
3 //!
4 //! To do so, we look for `JAVA_HOME` environment variable.
5 //! * If it exists, we recursively search for `jvm` library file inside `JAVA_HOME` directory.
6 //! * If it is not set, we use the following commmand to find actual JVM home directory:
7 //! ```bash
8 //! java -XshowSettings:properties -version | grep 'java.home'
9 //! ```
10 //! Then, we search for `jvm` as we have `JAVA_HOME`.
11 //!
12 //! On Windows, we also need to find `jvm.lib` file which is used while linking
13 //! at build time. This file is typically placed in `$JAVA_HOME/lib` directory.
14
15 use std::{
16 env,
17 path::{Path, PathBuf},
18 process::Command,
19 };
20
21 #[cfg(target_os = "windows")]
22 const EXPECTED_JVM_FILENAME: &str = "jvm.dll";
23 #[cfg(any(
24 target_os = "android",
25 target_os = "freebsd",
26 target_os = "linux",
27 target_os = "netbsd",
28 target_os = "openbsd"
29 ))]
30 const EXPECTED_JVM_FILENAME: &str = "libjvm.so";
31 #[cfg(target_os = "macos")]
32 const EXPECTED_JVM_FILENAME: &str = "libjli.dylib";
33
main()34 fn main() {
35 if cfg!(feature = "invocation") {
36 let java_home = match env::var("JAVA_HOME") {
37 Ok(java_home) => PathBuf::from(java_home),
38 Err(_) => find_java_home().expect(
39 "Failed to find Java home directory. \
40 Try setting JAVA_HOME",
41 ),
42 };
43
44 let libjvm_path =
45 find_libjvm(&java_home).expect("Failed to find libjvm.so. Check JAVA_HOME");
46
47 println!("cargo:rustc-link-search=native={}", libjvm_path.display());
48
49 // On Windows, we need additional file called `jvm.lib`
50 // and placed inside `JAVA_HOME\lib` directory.
51 if cfg!(windows) {
52 let lib_path = java_home.join("lib");
53 println!("cargo:rustc-link-search={}", lib_path.display());
54 }
55
56 println!("cargo:rerun-if-env-changed=JAVA_HOME");
57
58 // On MacOS, we need to link to libjli instead of libjvm as a workaround
59 // to a Java8 bug. See here for more information:
60 // https://bugs.openjdk.java.net/browse/JDK-7131356
61 if env::var("CARGO_CFG_TARGET_OS").unwrap() == "macos" {
62 println!("cargo:rustc-link-lib=dylib=jli");
63 } else {
64 println!("cargo:rustc-link-lib=dylib=jvm");
65 }
66 }
67 }
68
69 /// To find Java home directory, we call
70 /// `java -XshowSettings:properties -version` command and parse its output to
71 /// find the line `java.home=<some path>`.
find_java_home() -> Option<PathBuf>72 fn find_java_home() -> Option<PathBuf> {
73 Command::new("java")
74 .arg("-XshowSettings:properties")
75 .arg("-version")
76 .output()
77 .ok()
78 .and_then(|output| {
79 let stdout = String::from_utf8_lossy(&output.stdout);
80 let stderr = String::from_utf8_lossy(&output.stderr);
81 for line in stdout.lines().chain(stderr.lines()) {
82 if line.contains("java.home") {
83 let pos = line.find('=').unwrap() + 1;
84 let path = line[pos..].trim();
85 return Some(PathBuf::from(path));
86 }
87 }
88 None
89 })
90 }
91
find_libjvm<S: AsRef<Path>>(path: S) -> Option<PathBuf>92 fn find_libjvm<S: AsRef<Path>>(path: S) -> Option<PathBuf> {
93 let walker = walkdir::WalkDir::new(path).follow_links(true);
94
95 for entry in walker {
96 let entry = match entry {
97 Ok(entry) => entry,
98 Err(_e) => continue,
99 };
100
101 let file_name = entry.file_name().to_str().unwrap_or("");
102
103 if file_name == EXPECTED_JVM_FILENAME {
104 return entry.path().parent().map(Into::into);
105 }
106 }
107
108 None
109 }
110