@@ -40,41 +40,46 @@ public override bool Execute ()
4040 Log . LogMessage ( MessageImportance . Low , $ " { nameof ( MaximumJdkVersion ) } : { MaximumJdkVersion } ") ;
4141 Log . LogMessage ( MessageImportance . Low , $ " { nameof ( PropertyFile ) } : { PropertyFile } ") ;
4242
43- var maxVersion = GetMaxJdkVersion ( ) ;
43+ var maxVersion = GetVersion ( MaximumJdkVersion ) ;
44+
45+ string jarPath = null ;
46+ string javacPath = null ;
47+ string jdkJvmPath = null ;
48+ string includePath = null ;
49+
4450 var java_home = GetJavaHomePathFromEnvironment ( ) ;
45- if ( java_home != null ) {
46- var java_home_v = GetVersionFromPath ( java_home ) ;
47- if ( maxVersion != null && java_home_v != null && java_home_v > maxVersion ) {
48- Log . LogMessage ( MessageImportance . Low , $ " Skipping JAVA_HOME default value of `{ java_home } ` as it exceeds MaximumJdkVersion={ MaximumJdkVersion } .") ;
49- java_home = null ;
50- }
51- if ( java_home != null && ! Directory . Exists ( Path . Combine ( java_home , "include" ) ) ) {
52- Log . LogMessage ( MessageImportance . Low , $ " Skipping JAVA_HOME default value of `{ java_home } ` as it does not contain an `include` subdirectory.") ;
53- java_home = null ;
54- }
51+ if ( ! ValidateJdkPath ( maxVersion , java_home , out jarPath , out javacPath , out jdkJvmPath , out includePath ) ) {
52+ java_home = null ;
53+ }
54+
55+ if ( java_home == null &&
56+ ( java_home = GetJavaHomePathFromLibexec ( ) ) != null &&
57+ ! ValidateJdkPath ( maxVersion , java_home , out jarPath , out javacPath , out jdkJvmPath , out includePath ) ) {
58+ java_home = null ;
59+ }
60+
61+ if ( java_home == null &&
62+ ( java_home = GetJavaHomePathFromAlternatives ( ) ) != null &&
63+ ! ValidateJdkPath ( maxVersion , java_home , out jarPath , out javacPath , out jdkJvmPath , out includePath ) ) {
64+ java_home = null ;
65+ }
66+
67+ if ( java_home == null &&
68+ ( java_home = GetJavaHomePathFromMachine ( maxVersion ) ) != null &&
69+ ! ValidateJdkPath ( maxVersion , java_home , out jarPath , out javacPath , out jdkJvmPath , out includePath ) ) {
70+ java_home = null ;
5571 }
56- java_home = java_home ?? GetJavaHomePathFromMachine ( maxVersion ) ;
5772
58- if ( string . IsNullOrEmpty ( java_home ) ) {
73+ if ( java_home == null ) {
5974 Log . LogError ( "Could not determine JAVA_HOME location. Please set JdksRoot or export the JAVA_HOME environment variable." ) ;
6075 return false ;
6176 }
6277
6378 var includes = new List < string > ( ) {
64- Path . Combine ( java_home , "include" ) ,
79+ includePath ,
6580 } ;
66- includes . AddRange ( Directory . GetDirectories ( includes [ 0 ] ) ) ;
81+ includes . AddRange ( Directory . GetDirectories ( includePath ) ) ;
6782
68- var jarPath = FindExecutablesInDirectory ( Path . Combine ( java_home , "bin" ) , "jar" ) . First ( ) ;
69- var javacPath = FindExecutablesInDirectory ( Path . Combine ( java_home , "bin" ) , "javac" ) . First ( ) ;
70- var jdkJvmPaths = OS . IsMacOS
71- ? FindLibrariesInDirectory ( java_home , "jli" )
72- : FindLibrariesInDirectory ( Path . Combine ( java_home , "jre" ) , "jvm" ) ;
73- var jdkJvmPath = jdkJvmPaths . First ( ) ;
74-
75- FileExists ( jarPath ) ;
76- FileExists ( javacPath ) ;
77- FileExists ( jdkJvmPath ) ;
7883 if ( Log . HasLoggedErrors ) {
7984 return false ;
8085 }
@@ -92,26 +97,25 @@ public override bool Execute ()
9297 return ! Log . HasLoggedErrors ;
9398 }
9499
95- Version GetMaxJdkVersion ( )
100+ Version GetVersion ( string value )
96101 {
97- if ( string . IsNullOrEmpty ( MaximumJdkVersion ) )
102+ if ( string . IsNullOrEmpty ( value ) )
98103 return null ;
99- if ( ! MaximumJdkVersion . Contains ( "." ) ) {
100- MaximumJdkVersion += ".0" ;
104+ if ( ! value . Contains ( "." ) ) {
105+ value += ".0" ;
101106 }
102- return new Version ( MaximumJdkVersion ) ;
107+ Version v ;
108+ if ( Version . TryParse ( value , out v ) )
109+ return v ;
110+ return null ;
103111 }
104112
105113 Version GetVersionFromPath ( string path )
106114 {
107115 var m = VersionExtractor . Match ( path ) ;
108116 if ( ! m . Success )
109117 return null ;
110- Version v ;
111- if ( ! Version . TryParse ( m . Groups [ "version" ] . Value , out v ) ) {
112- return null ;
113- }
114- return v ;
118+ return GetVersion ( m . Groups [ "version" ] . Value ) ;
115119 }
116120
117121 void FileExists ( string path )
@@ -150,6 +154,56 @@ void WriteMakeFragmentFile (string jarPath, string javacPath, string jdkJvmPath,
150154 }
151155 }
152156
157+ bool ValidateJdkPath ( Version maxVersion , string java_home )
158+ {
159+ return ValidateJdkPath ( maxVersion , java_home ,
160+ out _ , out _ , out _ , out _ ) ;
161+ }
162+
163+ bool ValidateJdkPath ( Version maxVersion , string java_home ,
164+ out string jarPath , out string javacPath , out string jdkJvmPath , out string includePath )
165+ {
166+ jarPath = javacPath = jdkJvmPath = includePath = null ;
167+
168+ if ( string . IsNullOrEmpty ( java_home ) || ! Directory . Exists ( java_home ) )
169+ return false ;
170+
171+ var pathVersion = GetVersionFromPath ( java_home ) ;
172+ if ( maxVersion != null && pathVersion != null && pathVersion > maxVersion ) {
173+ Log . LogMessage ( MessageImportance . Low ,
174+ $ " Skipping JAVA_HOME value of `{ java_home } ` as it exceeds MaximumJdkVersion={ MaximumJdkVersion } .") ;
175+ return false ;
176+ }
177+
178+ jarPath = FindExecutablesInDirectory ( Path . Combine ( java_home , "bin" ) , "jar" ) . FirstOrDefault ( ) ;
179+ javacPath = FindExecutablesInDirectory ( Path . Combine ( java_home , "bin" ) , "javac" ) . FirstOrDefault ( ) ;
180+ var jdkJvmPaths = OS . IsMacOS
181+ ? FindLibrariesInDirectory ( java_home , "jli" )
182+ : FindLibrariesInDirectory ( Path . Combine ( java_home , "jre" ) , "jvm" ) ;
183+ jdkJvmPath = jdkJvmPaths . FirstOrDefault ( ) ;
184+ includePath = Path . Combine ( java_home , "include" ) ;
185+
186+ if ( jarPath == null ) {
187+ Log . LogMessage ( MessageImportance . Low , $ " Skipping JAVA_HOME value of `{ java_home } ` as `jar` could not be found.") ;
188+ return false ;
189+ }
190+ if ( javacPath == null ) {
191+ Log . LogMessage ( MessageImportance . Low , $ " Skipping JAVA_HOME value of `{ java_home } ` as `javac` could not be found.") ;
192+ return false ;
193+ }
194+ if ( jdkJvmPath == null ) {
195+ var jvm = OS . IsMacOS ? "libjli.dylib" : string . Format ( OS . NativeLibraryFormat , "jvm" ) ;
196+ Log . LogMessage ( MessageImportance . Low , $ " Skipping JAVA_HOME value of `{ java_home } ` as `{ jvm } could not be found.") ;
197+ return false ;
198+ }
199+ if ( ! Directory . Exists ( includePath ) ) {
200+ Log . LogMessage ( MessageImportance . Low , $ " Skipping JAVA_HOME value of `{ java_home } ` as the `include` directory could not be found.") ;
201+ return false ;
202+ }
203+
204+ return true ;
205+ }
206+
153207 string GetJavaHomePathFromEnvironment ( )
154208 {
155209 var java_home = Environment . GetEnvironmentVariable ( "JAVA_HOME" ) ;
@@ -158,24 +212,74 @@ string GetJavaHomePathFromEnvironment ()
158212 return null ;
159213 }
160214
215+ // macOS
216+ string GetJavaHomePathFromLibexec ( )
217+ {
218+ var java_home = Path . GetFullPath ( "/usr/libexec/java_home" ) ;
219+ if ( ! File . Exists ( java_home ) ) {
220+ return null ;
221+ }
222+ string path = null ;
223+ Exec ( java_home , "" , ( o , e ) => {
224+ if ( string . IsNullOrEmpty ( e . Data ) )
225+ return ;
226+ Log . LogMessage ( MessageImportance . Low , $ " { e . Data } ") ;
227+ path = e . Data ;
228+ } ) ;
229+ return path ;
230+ }
231+
232+ // Linux
233+ string GetJavaHomePathFromAlternatives ( )
234+ {
235+ var alternatives = Path . GetFullPath ( "/etc/alternatives/java" ) ;
236+ if ( ! File . Exists ( alternatives ) )
237+ return null ;
238+ string targetJava = null ;
239+ Exec ( "readlink" , $ "\" { alternatives } \" ", ( o , e ) => {
240+ if ( string . IsNullOrEmpty ( e . Data ) )
241+ return ;
242+ Log . LogMessage ( MessageImportance . Low , $ " { e . Data } ") ;
243+ targetJava = e . Data ;
244+ } ) ;
245+ if ( string . IsNullOrEmpty ( targetJava ) )
246+ return null ;
247+ return GetJavaHomePathFromJava ( targetJava ) ;
248+ }
249+
161250 string GetJavaHomePathFromMachine ( Version maxVersion )
162251 {
163252 var java_homes = GetJavaHomePathsFromDirectory ( JdksRoot )
164253 . Concat ( GetJavaHomePathsFromJava ( ) )
165254 . Concat ( GetJavaHomePathsFromWindowsRegistry ( ) )
166- . Distinct ( )
167255 . Where ( d => Directory . Exists ( d ) )
168- . Select ( jh => new {
256+ . Distinct ( )
257+ . ToList ( ) ;
258+
259+ foreach ( var p in java_homes ) {
260+ Log . LogMessage ( MessageImportance . Low , $ " Possible JAVA_HOME location: { p } ") ;
261+ }
262+
263+ var versionComparer = new ComparisonComparer < JdkComparisonInfo > ( ( x , y ) => {
264+ int r = 0 ;
265+ if ( x . Version != null && y . Version != null )
266+ r = x . Version . CompareTo ( y . Version ) ;
267+ return r ;
268+ } ) ;
269+
270+ java_homes = java_homes . Where ( d => ValidateJdkPath ( maxVersion , d ) )
271+ . Select ( jh => new JdkComparisonInfo {
169272 Path = jh ,
170273 Version = GetVersionFromPath ( jh ) ,
171274 } )
172- . Where ( v => maxVersion == null ? true : v . Version <= maxVersion )
173- . OrderByDescending ( v => v . Version )
275+ . Where ( v => ( maxVersion == null || v . Version == null ) ? true : v . Version <= maxVersion )
276+ . OrderByDescending ( v => v , versionComparer )
277+ . ThenByDescending ( v => Directory . GetLastWriteTimeUtc ( v . Path ) )
174278 . Select ( v => v . Path )
175279 . ToList ( ) ;
176280
177281 foreach ( var p in java_homes ) {
178- Log . LogMessage ( MessageImportance . Low , $ " Possible JAVA_HOME location: { p } ") ;
282+ Log . LogMessage ( MessageImportance . Low , $ " Filtered JAVA_HOME location: { p } ") ;
179283 }
180284
181285 return java_homes . FirstOrDefault ( ) ;
@@ -201,26 +305,32 @@ IEnumerable<string> GetJavaHomePathsFromJava ()
201305 . SelectMany ( p => FindExecutablesInDirectory ( p , "java" ) ) ;
202306
203307 foreach ( var exe in javas ) {
204- const string JavaHome = "java.home = " ;
205- string java_home = null ;
206- Exec ( exe , "-XshowSettings:properties -version" , ( o , e ) => {
207- int i = e . Data ? . IndexOf ( JavaHome ) ?? - 1 ;
208- if ( i < 0 )
209- return ;
210- Log . LogMessage ( MessageImportance . Low , $ " { e . Data } ") ;
211- java_home = e . Data . Substring ( JavaHome . Length + i ) ;
212- // `java -XshowSettings:properties -version | grep java.home` ends with `/jre` on macOS.
213- // We need the parent dir so we can properly lookup the `include` directories
214- if ( java_home . EndsWith ( "jre" , StringComparison . OrdinalIgnoreCase ) ) {
215- java_home = Path . GetDirectoryName ( java_home ) ;
216- }
217- } ) ;
308+ var java_home = GetJavaHomePathFromJava ( exe ) ;
218309 if ( string . IsNullOrEmpty ( java_home ) )
219310 continue ;
220311 yield return java_home ;
221312 }
222313 }
223314
315+ string GetJavaHomePathFromJava ( string java )
316+ {
317+ const string JavaHome = "java.home = " ;
318+ string java_home = null ;
319+ Exec ( java , "-XshowSettings:properties -version" , ( o , e ) => {
320+ int i = e . Data ? . IndexOf ( JavaHome ) ?? - 1 ;
321+ if ( i < 0 )
322+ return ;
323+ Log . LogMessage ( MessageImportance . Low , $ " { e . Data } ") ;
324+ java_home = e . Data . Substring ( JavaHome . Length + i ) ;
325+ // `java -XshowSettings:properties -version 2>&1 | grep java.home` ends with `/jre` on macOS.
326+ // We need the parent dir so we can properly lookup the `include` directories
327+ if ( java_home . EndsWith ( "jre" , StringComparison . OrdinalIgnoreCase ) ) {
328+ java_home = Path . GetDirectoryName ( java_home ) ;
329+ }
330+ } ) ;
331+ return java_home ;
332+ }
333+
224334 void Exec ( string java , string arguments , DataReceivedEventHandler output )
225335 {
226336 Log . LogMessage ( MessageImportance . Low , $ " Tool { java } execution started with arguments: { arguments } ") ;
@@ -346,5 +456,25 @@ IEnumerable<string> FindLibrariesInDirectory (string dir, string libraryName)
346456 var library = string . Format ( OS . NativeLibraryFormat , libraryName ) ;
347457 return Directory . EnumerateFiles ( dir , library , SearchOption . AllDirectories ) ;
348458 }
459+
460+ class JdkComparisonInfo {
461+ public string Path ;
462+ public Version Version ;
463+ }
464+ }
465+
466+ class ComparisonComparer < T > : IComparer < T > {
467+
468+ Comparison < T > comparison ;
469+
470+ public ComparisonComparer ( Comparison < T > comparison )
471+ {
472+ this . comparison = comparison ;
473+ }
474+
475+ public int Compare ( T x , T y )
476+ {
477+ return comparison ( x , y ) ;
478+ }
349479 }
350480}
0 commit comments