@@ -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,103 @@ 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})" ) ) ) ?;
135+
136+ let java_exec_path = std:: str:: from_utf8 ( & output. stdout ) ?
137+ // Windows will return multiple lines if there are multiple `java` in the PATH.
138+ . lines ( )
139+ // The first line is the one that would be run, so take just that line.
140+ . next ( )
141+ . unwrap ( )
142+ . trim ( ) ;
180143
181- let mut command = Command :: new ( command_str) ;
144+ if java_exec_path. is_empty ( ) {
145+ return Err ( JavaLocatorError :: new (
146+ "Java is not installed or not in the system PATH" . into ( ) ,
147+ ) ) ;
148+ }
149+
150+ let mut home_path = follow_symlinks ( java_exec_path) ;
151+
152+ // Here we should have found ourselves in a directory like /usr/lib/jvm/java-8-oracle/jre/bin/java
153+ home_path. pop ( ) ;
154+ home_path. pop ( ) ;
155+
156+ home_path
157+ . into_os_string ( )
158+ . into_string ( )
159+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
160+ }
182161
183- if !is_macos ( ) {
184- command. arg ( "java" ) ;
162+ #[ cfg( target_os = "macos" ) ]
163+ fn do_locate_java_home ( ) -> Result < String > {
164+ let output = Command :: new ( "/usr/libexec/java_home" )
165+ . output ( )
166+ . map_err ( |e| {
167+ JavaLocatorError :: new ( format ! (
168+ "Failed to run command `/usr/libexec/java_home` ({e})"
169+ ) )
170+ } ) ?;
171+
172+ let java_exec_path = std:: str:: from_utf8 ( & output. stdout ) ?. trim ( ) ;
173+
174+ if java_exec_path. is_empty ( ) {
175+ return Err ( JavaLocatorError :: new (
176+ "Java is not installed or not in the system PATH" . into ( ) ,
177+ ) ) ;
185178 }
186179
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- } ) ?;
180+ let home_path = follow_symlinks ( java_exec_path) ;
181+
182+ home_path
183+ . into_os_string ( )
184+ . into_string ( )
185+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
186+ }
187+
188+ // TODO: double check this
189+ #[ cfg( not( any( target_os = "windows" , target_os = "macos" ) ) ) ] // Unix
190+ fn do_locate_java_home ( ) -> Result < String > {
191+ let output = Command :: new ( "which" )
192+ . arg ( "java" )
193+ . output ( )
194+ . map_err ( |e| JavaLocatorError :: new ( format ! ( "Failed to run command `which` ({e})" ) ) ) ?;
195+ let java_exec_path = std:: str:: from_utf8 ( & output. stdout ) ?. trim ( ) ;
207196
208- // Return early in case that the java executable is not found
209197 if java_exec_path. is_empty ( ) {
210- Err ( errors :: JavaLocatorError :: new (
211- "Java is not installed or not added in the system PATH" ,
212- ) ) ?
198+ return Err ( JavaLocatorError :: new (
199+ "Java is not installed or not in the system PATH" . into ( ) ,
200+ ) ) ;
213201 }
214202
215- let mut test_path = PathBuf :: from ( java_exec_path. trim ( ) ) ;
203+ let mut home_path = follow_symlinks ( java_exec_path) ;
204+
205+ // Here we should have found ourselves in a directory like /usr/lib/jvm/java-8-oracle/jre/bin/java
206+ home_path. pop ( ) ;
207+ home_path. pop ( ) ;
216208
209+ home_path
210+ . into_os_string ( )
211+ . into_string ( )
212+ . map_err ( |path| JavaLocatorError :: new ( format ! ( "Java path {path:?} is invalid utf8" ) ) )
213+ }
214+
215+ // Its not clear to me which systems need this so for now its run on all systems.
216+ fn follow_symlinks ( path : & str ) -> PathBuf {
217+ let mut test_path = PathBuf :: from ( path) ;
217218 while let Ok ( path) = test_path. read_link ( ) {
218219 test_path = if path. is_absolute ( ) {
219220 path
@@ -223,55 +224,39 @@ fn do_locate_java_home() -> errors::Result<String> {
223224 test_path
224225 } ;
225226 }
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- }
227+ test_path
240228}
241229
242230/// 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)
231+ pub fn locate_jvm_dyn_library ( ) -> Result < String > {
232+ if cfg ! ( target_os = "windows" ) {
233+ locate_file ( "jvm.dll" )
234+ } else {
235+ locate_file ( "libjvm.*" )
236+ }
247237}
248238
249239/// Returns the path that contains the file with the provided name.
250240///
251241/// This function argument can be a wildcard.
252- pub fn locate_file ( file_name : & str ) -> errors :: Result < String > {
242+ pub fn locate_file ( file_name : & str ) -> Result < String > {
253243 // Find the JAVA_HOME
254244 let java_home = locate_java_home ( ) ?;
255245
256246 let query = format ! ( "{}/**/{}" , Pattern :: escape( & java_home) , file_name) ;
257247
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 ( ) )
248+ let path = glob ( & query) ?. filter_map ( |x| x. ok ( ) ) . next ( ) . ok_or_else ( || {
249+ JavaLocatorError :: new ( format ! (
250+ "Could not find the {file_name} library in any subdirectory of {java_home}" ,
251+ ) )
252+ } ) ?;
253+
254+ let parent_path = path. parent ( ) . unwrap ( ) ;
255+ match parent_path. to_str ( ) {
256+ Some ( parent_path) => Ok ( parent_path. to_owned ( ) ) ,
257+ None => Err ( JavaLocatorError :: new ( format ! (
258+ "Java path {parent_path:?} is invalid utf8"
259+ ) ) ) ,
275260 }
276261}
277262
0 commit comments