@@ -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,9 +104,9 @@ 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+ if cfg ! ( target_os = "windows" ) {
150108 "jvm.dll"
151- } else if is_macos ( ) {
109+ } else if cfg ! ( target_os = "macos" ) {
152110 "libjvm.dylib"
153111 } else {
154112 "libjvm.so"
@@ -160,60 +118,102 @@ pub fn get_jvm_dyn_lib_file_name() -> &'static str {
160118/// If `JAVA_HOME` env var is defined, the function returns it without any checks whether the var points to a valid directory or not.
161119///
162120/// 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 > {
121+ pub fn locate_java_home ( ) -> Result < String > {
164122 match & env:: var ( "JAVA_HOME" ) {
165123 Ok ( s) if s. is_empty ( ) => do_locate_java_home ( ) ,
166124 Ok ( java_home_env_var) => Ok ( java_home_env_var. clone ( ) ) ,
167125 Err ( _) => do_locate_java_home ( ) ,
168126 }
169127}
170128
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- } ;
129+ #[ cfg( target_os = "windows" ) ]
130+ fn do_locate_java_home ( ) -> Result < String > {
131+ let output = Command :: new ( "where" )
132+ . arg ( "java" )
133+ . output ( )
134+ . map_err ( |e| JavaLocatorError :: new ( format ! ( "Failed to run command `where` ({e})" ) ) ) ?;
180135
181- let mut command = Command :: new ( command_str) ;
136+ let java_exec_path_raw = std:: str:: from_utf8 ( & output. stdout ) ?;
137+ java_exec_path_validation ( java_exec_path_raw) ?;
182138
183- if !is_macos ( ) {
184- command. arg ( "java" ) ;
139+ // Windows will return multiple lines if there are multiple `java` in the PATH.
140+ let paths_found = java_exec_path_raw. lines ( ) . count ( ) ;
141+ if paths_found > 1 {
142+ eprintln ! ( "WARNING: java_locator found {paths_found} possible java locations. Using the first one. To silence this warning set JAVA_HOME env var." )
185143 }
186144
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- } ) ?;
145+ let java_exec_path = java_exec_path_raw
146+ . lines ( )
147+ // The first line is the one that would be run, so take just that line.
148+ . next ( )
149+ . expect ( "gauranteed to have at least one line by java_exec_path_validation" )
150+ . trim ( ) ;
151+
152+ let mut home_path = follow_symlinks ( java_exec_path) ;
153+
154+ home_path. pop ( ) ;
155+ home_path. pop ( ) ;
156+
157+ home_path
158+ . into_os_string ( )
159+ . into_string ( )
160+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
161+ }
207162
208- // Return early in case that the java executable is not found
209- if java_exec_path. is_empty ( ) {
210- Err ( errors:: JavaLocatorError :: new (
211- "Java is not installed or not added in the system PATH" ,
212- ) ) ?
163+ #[ cfg( target_os = "macos" ) ]
164+ fn do_locate_java_home ( ) -> Result < String > {
165+ let output = Command :: new ( "/usr/libexec/java_home" )
166+ . output ( )
167+ . map_err ( |e| {
168+ JavaLocatorError :: new ( format ! (
169+ "Failed to run command `/usr/libexec/java_home` ({e})"
170+ ) )
171+ } ) ?;
172+
173+ let java_exec_path = std:: str:: from_utf8 ( & output. stdout ) ?. trim ( ) ;
174+
175+ java_exec_path_validation ( java_exec_path) ?;
176+ let home_path = follow_symlinks ( java_exec_path) ;
177+
178+ home_path
179+ . into_os_string ( )
180+ . into_string ( )
181+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
182+ }
183+
184+ #[ cfg( not( any( target_os = "windows" , target_os = "macos" ) ) ) ] // Unix
185+ fn do_locate_java_home ( ) -> Result < String > {
186+ let output = Command :: new ( "which" )
187+ . arg ( "java" )
188+ . output ( )
189+ . map_err ( |e| JavaLocatorError :: new ( format ! ( "Failed to run command `which` ({e})" ) ) ) ?;
190+ let java_exec_path = std:: str:: from_utf8 ( & output. stdout ) ?. trim ( ) ;
191+
192+ java_exec_path_validation ( java_exec_path) ?;
193+ let mut home_path = follow_symlinks ( java_exec_path) ;
194+
195+ // Here we should have found ourselves in a directory like /usr/lib/jvm/java-8-oracle/jre/bin/java
196+ home_path. pop ( ) ;
197+ home_path. pop ( ) ;
198+
199+ home_path
200+ . into_os_string ( )
201+ . into_string ( )
202+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
203+ }
204+
205+ fn java_exec_path_validation ( path : & str ) -> Result < ( ) > {
206+ if path. is_empty ( ) {
207+ return Err ( JavaLocatorError :: new (
208+ "Java is not installed or not in the system PATH" . into ( ) ,
209+ ) ) ;
213210 }
214211
215- let mut test_path = PathBuf :: from ( java_exec_path. trim ( ) ) ;
212+ Ok ( ( ) )
213+ }
216214
215+ fn follow_symlinks ( path : & str ) -> PathBuf {
216+ let mut test_path = PathBuf :: from ( path) ;
217217 while let Ok ( path) = test_path. read_link ( ) {
218218 test_path = if path. is_absolute ( ) {
219219 path
@@ -223,55 +223,39 @@ fn do_locate_java_home() -> errors::Result<String> {
223223 test_path
224224 } ;
225225 }
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- }
226+ test_path
240227}
241228
242229/// 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.*" } ;
245-
246- locate_file ( jvm_dyn_lib_file_name)
230+ pub fn locate_jvm_dyn_library ( ) -> Result < String > {
231+ if cfg ! ( target_os = "windows" ) {
232+ locate_file ( "jvm.dll" )
233+ } else {
234+ locate_file ( "libjvm.*" )
235+ }
247236}
248237
249238/// Returns the path that contains the file with the provided name.
250239///
251240/// This function argument can be a wildcard.
252- pub fn locate_file ( file_name : & str ) -> errors :: Result < String > {
241+ pub fn locate_file ( file_name : & str ) -> Result < String > {
253242 // Find the JAVA_HOME
254243 let java_home = locate_java_home ( ) ?;
255244
256245 let query = format ! ( "{}/**/{}" , Pattern :: escape( & java_home) , file_name) ;
257246
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 ( ) )
247+ let path = glob ( & query) ?. filter_map ( |x| x. ok ( ) ) . next ( ) . ok_or_else ( || {
248+ JavaLocatorError :: new ( format ! (
249+ "Could not find the {file_name} library in any subdirectory of {java_home}" ,
250+ ) )
251+ } ) ?;
252+
253+ let parent_path = path. parent ( ) . unwrap ( ) ;
254+ match parent_path. to_str ( ) {
255+ Some ( parent_path) => Ok ( parent_path. to_owned ( ) ) ,
256+ None => Err ( JavaLocatorError :: new ( format ! (
257+ "Java path {parent_path:?} is invalid utf8"
258+ ) ) ) ,
275259 }
276260}
277261
0 commit comments