@@ -91,53 +91,11 @@ use std::env;
9191use std:: path:: PathBuf ;
9292use std:: process:: Command ;
9393
94+ use errors:: { JavaLocatorError , Result } ;
9495use glob:: { glob, Pattern } ;
95- use lazy_static:: lazy_static;
9696
9797pub mod errors;
9898
99- const WINDOWS : & ' static str = "windows" ;
100- const MACOS : & ' static str = "macos" ;
101- const ANDROID : & ' static str = "android" ;
102- const UNIX : & ' static str = "unix" ;
103-
104- lazy_static ! {
105- static ref TARGET_OS : String = {
106- let target_os_res = env:: var( "CARGO_CFG_TARGET_OS" ) ;
107- let tos = target_os_res. as_ref( ) . map( |x| & * * x) . unwrap_or_else( |_| {
108- if cfg!( windows) {
109- WINDOWS
110- } else if cfg!( target_os = "macos" ) {
111- MACOS
112- } else if cfg!( target_os = "android" ) {
113- ANDROID
114- } else {
115- UNIX
116- }
117- } ) ;
118-
119- tos. to_string( )
120- } ;
121- }
122-
123- fn is_windows ( ) -> bool {
124- & * TARGET_OS == WINDOWS
125- }
126-
127- fn is_macos ( ) -> bool {
128- & * TARGET_OS == MACOS
129- }
130-
131- #[ allow( dead_code) ]
132- fn is_android ( ) -> bool {
133- & * TARGET_OS == ANDROID
134- }
135-
136- #[ allow( dead_code) ]
137- fn is_unix ( ) -> bool {
138- & * TARGET_OS == UNIX
139- }
140-
14199/// Returns the name of the jvm dynamic library:
142100///
143101/// * libjvm.so for Linux
@@ -146,11 +104,16 @@ fn is_unix() -> bool {
146104///
147105/// * jvm.dll for Windows
148106pub fn get_jvm_dyn_lib_file_name ( ) -> & ' static str {
149- if is_windows ( ) {
107+ #[ cfg( target_os = "windows" ) ]
108+ {
150109 "jvm.dll"
151- } else if is_macos ( ) {
110+ }
111+ #[ cfg( target_os = "macos" ) ]
112+ {
152113 "libjvm.dylib"
153- } else {
114+ }
115+ #[ cfg( not( any( target_os = "windows" , target_os = "macos" ) ) ) ]
116+ {
154117 "libjvm.so"
155118 }
156119}
@@ -160,60 +123,103 @@ pub fn get_jvm_dyn_lib_file_name() -> &'static str {
160123/// If `JAVA_HOME` env var is defined, the function returns it without any checks whether the var points to a valid directory or not.
161124///
162125/// If `JAVA_HOME` is not defined, the function tries to locate it using the `java` executable.
163- pub fn locate_java_home ( ) -> errors :: Result < String > {
126+ pub fn locate_java_home ( ) -> Result < String > {
164127 match & env:: var ( "JAVA_HOME" ) {
165128 Ok ( s) if s. is_empty ( ) => do_locate_java_home ( ) ,
166129 Ok ( java_home_env_var) => Ok ( java_home_env_var. clone ( ) ) ,
167130 Err ( _) => do_locate_java_home ( ) ,
168131 }
169132}
170133
171- fn do_locate_java_home ( ) -> errors:: Result < String > {
172- // Prepare the command depending on the host
173- let command_str = if is_windows ( ) {
174- "where"
175- } else if is_macos ( ) {
176- "/usr/libexec/java_home"
177- } else {
178- "which"
179- } ;
134+ #[ cfg( target_os = "windows" ) ]
135+ fn do_locate_java_home ( ) -> Result < String > {
136+ let output = Command :: new ( "where" )
137+ . arg ( "java" )
138+ . output ( )
139+ . map_err ( |e| JavaLocatorError :: new ( format ! ( "Failed to run command `where` ({e})" ) ) ) ?;
140+
141+ let java_exec_path = std:: str:: from_utf8 ( & output. stdout ) ?
142+ // Windows will return multiple lines if there are multiple `java` in the PATH.
143+ . lines ( )
144+ // The first line is the one that would be run, so take just that line.
145+ . next ( )
146+ . unwrap ( )
147+ . trim ( ) ;
148+
149+ if java_exec_path. is_empty ( ) {
150+ return Err ( JavaLocatorError :: new (
151+ "Java is not installed or not in the system PATH" . into ( ) ,
152+ ) ) ;
153+ }
154+
155+ let mut home_path = follow_symlinks ( java_exec_path) ;
156+
157+ // Here we should have found ourselves in a directory like /usr/lib/jvm/java-8-oracle/jre/bin/java
158+ home_path. pop ( ) ;
159+ home_path. pop ( ) ;
160+
161+ home_path
162+ . into_os_string ( )
163+ . into_string ( )
164+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
165+ }
166+
167+ #[ cfg( target_os = "macos" ) ]
168+ fn do_locate_java_home ( ) -> Result < String > {
169+ let output = Command :: new ( "/usr/libexec/java_home" )
170+ . output ( )
171+ . map_err ( |e| {
172+ JavaLocatorError :: new ( format ! (
173+ "Failed to run command `/usr/libexec/java_home` ({e})"
174+ ) )
175+ } ) ?;
180176
181- let mut command = Command :: new ( command_str ) ;
177+ let java_exec_path = std :: str :: from_utf8 ( & output . stdout ) ? . trim ( ) ;
182178
183- if !is_macos ( ) {
184- command. arg ( "java" ) ;
179+ if java_exec_path. is_empty ( ) {
180+ return Err ( JavaLocatorError :: new (
181+ "Java is not installed or not in the system PATH" . into ( ) ,
182+ ) ) ;
185183 }
186184
187- let output = command. output ( ) . map_err ( |error| {
188- let message = format ! (
189- "Command '{}' is not found in the system PATH ({})" ,
190- command_str, error
191- ) ;
192- errors:: JavaLocatorError :: new ( & message)
193- } ) ?;
194- let java_exec_path = String :: from_utf8 ( output. stdout ) . map ( |jp| {
195- let mut lines: Vec < & str > = jp. lines ( ) . collect ( ) ;
196- if lines. len ( ) > 1 {
197- println ! (
198- "WARNING: java_locator found {} possible java locations: {}. Using the last one." ,
199- lines. len( ) ,
200- lines. join( ", " )
201- ) ;
202- lines. remove ( lines. len ( ) - 1 ) . to_string ( )
203- } else {
204- jp
205- }
206- } ) ?;
185+ let home_path = follow_symlinks ( java_exec_path) ;
186+
187+ home_path
188+ . into_os_string ( )
189+ . into_string ( )
190+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
191+ }
192+
193+ // TODO: double check this
194+ #[ cfg( not( any( target_os = "windows" , target_os = "macos" ) ) ) ] // Unix
195+ fn do_locate_java_home ( ) -> Result < String > {
196+ let output = Command :: new ( "which" )
197+ . arg ( "java" )
198+ . output ( )
199+ . map_err ( |e| JavaLocatorError :: new ( format ! ( "Failed to run command `which` ({e})" ) ) ) ?;
200+ let java_exec_path = std:: str:: from_utf8 ( & output. stdout ) ?. trim ( ) ;
207201
208- // Return early in case that the java executable is not found
209202 if java_exec_path. is_empty ( ) {
210- Err ( errors :: JavaLocatorError :: new (
211- "Java is not installed or not added in the system PATH" ,
212- ) ) ?
203+ return Err ( JavaLocatorError :: new (
204+ "Java is not installed or not in the system PATH" . into ( ) ,
205+ ) ) ;
213206 }
214207
215- let mut test_path = PathBuf :: from ( java_exec_path. trim ( ) ) ;
208+ let mut home_path = follow_symlinks ( java_exec_path) ;
209+
210+ // Here we should have found ourselves in a directory like /usr/lib/jvm/java-8-oracle/jre/bin/java
211+ home_path. pop ( ) ;
212+ home_path. pop ( ) ;
213+
214+ home_path
215+ . into_os_string ( )
216+ . into_string ( )
217+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
218+ }
216219
220+ // Its not clear to me which systems need this so for now its run on all systems.
221+ fn follow_symlinks ( path : & str ) -> PathBuf {
222+ let mut test_path = PathBuf :: from ( path) ;
217223 while let Ok ( path) = test_path. read_link ( ) {
218224 test_path = if path. is_absolute ( ) {
219225 path
@@ -223,55 +229,40 @@ fn do_locate_java_home() -> errors::Result<String> {
223229 test_path
224230 } ;
225231 }
226-
227- if !is_macos ( ) {
228- // Here we should have found ourselves in a directory like /usr/lib/jvm/java-8-oracle/jre/bin/java
229- test_path. pop ( ) ;
230- test_path. pop ( ) ;
231- }
232-
233- match test_path. to_str ( ) {
234- Some ( s) => Ok ( String :: from ( s) ) ,
235- None => Err ( errors:: JavaLocatorError :: new ( & format ! (
236- "Could not convert path {:?} to String" ,
237- test_path
238- ) ) ) ,
239- }
232+ test_path
240233}
241234
242235/// Returns the path that contains the `libjvm.so` (or `jvm.dll` in windows).
243- pub fn locate_jvm_dyn_library ( ) -> errors:: Result < String > {
244- let jvm_dyn_lib_file_name = if is_windows ( ) { "jvm.dll" } else { "libjvm.*" } ;
236+ pub fn locate_jvm_dyn_library ( ) -> Result < String > {
237+ #[ cfg( target_os = "windows" ) ]
238+ let jvm_dyn_lib_file_name = "jvm.dll" ;
239+ #[ cfg( not( target_os = "windows" ) ) ]
240+ let jvm_dyn_lib_file_name = "libjvm.*" ;
245241
246242 locate_file ( jvm_dyn_lib_file_name)
247243}
248244
249245/// Returns the path that contains the file with the provided name.
250246///
251247/// This function argument can be a wildcard.
252- pub fn locate_file ( file_name : & str ) -> errors :: Result < String > {
248+ pub fn locate_file ( file_name : & str ) -> Result < String > {
253249 // Find the JAVA_HOME
254250 let java_home = locate_java_home ( ) ?;
255251
256252 let query = format ! ( "{}/**/{}" , Pattern :: escape( & java_home) , file_name) ;
257253
258- let paths_vec: Vec < String > = glob ( & query) ?
259- . filter_map ( Result :: ok)
260- . map ( |path_buf| {
261- let mut pb = path_buf. clone ( ) ;
262- pb. pop ( ) ;
263- pb. to_str ( ) . unwrap_or ( "" ) . to_string ( )
264- } )
265- . filter ( |s : & String | !s. is_empty ( ) )
266- . collect ( ) ;
267-
268- if paths_vec. is_empty ( ) {
269- Err ( errors:: JavaLocatorError :: new ( & format ! (
270- "Could not find the {} library in any subdirectory of {}" ,
271- file_name, java_home
272- ) ) )
273- } else {
274- Ok ( paths_vec[ 0 ] . clone ( ) )
254+ let path = glob ( & query) ?. filter_map ( |x| x. ok ( ) ) . next ( ) . ok_or_else ( || {
255+ JavaLocatorError :: new ( format ! (
256+ "Could not find the {file_name} library in any subdirectory of {java_home}" ,
257+ ) )
258+ } ) ?;
259+
260+ let parent_path = path. parent ( ) . unwrap ( ) ;
261+ match parent_path. to_str ( ) {
262+ Some ( parent_path) => Ok ( parent_path. to_owned ( ) ) ,
263+ None => Err ( JavaLocatorError :: new ( format ! (
264+ "Java path {parent_path:?} is invalid utf8"
265+ ) ) ) ,
275266 }
276267}
277268
0 commit comments