• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = "freebsd",
25     target_os = "linux",
26     target_os = "netbsd",
27     target_os = "openbsd"
28 ))]
29 const EXPECTED_JVM_FILENAME: &str = "libjvm.so";
30 #[cfg(target_os = "macos")]
31 const EXPECTED_JVM_FILENAME: &str = "libjli.dylib";
32 
main()33 fn main() {
34     if cfg!(feature = "invocation") {
35         let java_home = match env::var("JAVA_HOME") {
36             Ok(java_home) => PathBuf::from(java_home),
37             Err(_) => find_java_home().expect(
38                 "Failed to find Java home directory. \
39                  Try setting JAVA_HOME",
40             ),
41         };
42 
43         let libjvm_path =
44             find_libjvm(&java_home).expect("Failed to find libjvm.so. Check JAVA_HOME");
45 
46         println!("cargo:rustc-link-search=native={}", libjvm_path.display());
47 
48         // On Windows, we need additional file called `jvm.lib`
49         // and placed inside `JAVA_HOME\lib` directory.
50         if cfg!(windows) {
51             let lib_path = java_home.join("lib");
52             println!("cargo:rustc-link-search={}", lib_path.display());
53         }
54 
55         println!("cargo:rerun-if-env-changed=JAVA_HOME");
56 
57         // On MacOS, we need to link to libjli instead of libjvm as a workaround
58         // to a Java8 bug. See here for more information:
59         // https://bugs.openjdk.java.net/browse/JDK-7131356
60         if cfg!(target_os = "macos") {
61             println!("cargo:rustc-link-lib=dylib=jli");
62         } else {
63             println!("cargo:rustc-link-lib=dylib=jvm");
64         }
65     }
66 }
67 
68 /// To find Java home directory, we call
69 /// `java -XshowSettings:properties -version` command and parse its output to
70 /// find the line `java.home=<some path>`.
find_java_home() -> Option<PathBuf>71 fn find_java_home() -> Option<PathBuf> {
72     Command::new("java")
73         .arg("-XshowSettings:properties")
74         .arg("-version")
75         .output()
76         .ok()
77         .and_then(|output| {
78             let stdout = String::from_utf8_lossy(&output.stdout);
79             let stderr = String::from_utf8_lossy(&output.stderr);
80             for line in stdout.lines().chain(stderr.lines()) {
81                 if line.contains("java.home") {
82                     let pos = line.find('=').unwrap() + 1;
83                     let path = line[pos..].trim();
84                     return Some(PathBuf::from(path));
85                 }
86             }
87             None
88         })
89 }
90 
find_libjvm<S: AsRef<Path>>(path: S) -> Option<PathBuf>91 fn find_libjvm<S: AsRef<Path>>(path: S) -> Option<PathBuf> {
92     let walker = walkdir::WalkDir::new(path).follow_links(true);
93 
94     for entry in walker {
95         let entry = match entry {
96             Ok(entry) => entry,
97             Err(_e) => continue,
98         };
99 
100         let file_name = entry.file_name().to_str().unwrap_or("");
101 
102         if file_name == EXPECTED_JVM_FILENAME {
103             return entry.path().parent().map(Into::into);
104         }
105     }
106 
107     None
108 }
109