Skip to content

Commit dca30d9

Browse files
authored
[Xamarin.Android.Tools.AndroidSdk] Probe for Zulu JDK (#114)
Context: https://www.azul.com/downloads/zulu-community/ Commit 237642c removed support for the ancient `microsoft_dist_openjdk_*` versions, replacing it with the newer [Microsoft OpenJDK][0]. One problem with this change is one of *compatibility*: `microsoft_dist_openjdk_` was a build of JDK 1.8, while Microsoft OpenJDK only provides builds for JDK 11 and later. This is a problem, as the [Visual Studio Android Designer][1] currently requires JDK 1.8, and is not yet compatible with JDK 11. Add support to probe for for [Azul Zulu OpenJDK][2] installation directories, as Zulu provides updated JDK 1.8 installers. Additionally, refactor and centralize the JDK Location logic. Previously, `JdkInfo.GetKnownSystemJdkInfos()` would return all of the Windows installation locations before the macOS/Linux/etc. locations, which made it possible for Windows and non-Windows platforms to return paths in an inconsistent order. Introduce a new `JdkLocations` helper, and add new subclasses for each specific JDK location, so that `JdkInfo.GetKnownSystemJdkInfos()` can be *unified* across macOS/Linux & Windows, increasing consistency. Add a new `tools/ls-jdks` tool, to make it easier to print all probed JDK locations. [0]: https://www.microsoft.com/openjdk [1]: https://docs.microsoft.com/en-us/xamarin/android/user-interface/android-designer/designer-walkthrough?tabs=windows [2]: https://www.azul.com/downloads/zulu-community/?package=jdk
1 parent 237642c commit dca30d9

File tree

17 files changed

+430
-310
lines changed

17 files changed

+430
-310
lines changed

Xamarin.Android.Tools.sln

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Microsoft Visual Studio Solution File, Format Version 12.00
1+
Microsoft Visual Studio Solution File, Format Version 12.00
22
# Visual Studio Version 16
33
VisualStudioVersion = 16.0.30709.64
44
MinimumVisualStudioVersion = 10.0.40219.1
@@ -10,6 +10,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Android.Build.Bas
1010
EndProject
1111
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Build.BaseTasks-Tests", "tests\Microsoft.Android.Build.BaseTasks-Tests\Microsoft.Android.Build.BaseTasks-Tests.csproj", "{4F5FD01C-B5A6-4F9F-91CE-3E78B3F942AC}"
1212
EndProject
13+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{19FE1B44-DB71-4F97-A07E-085888690DAE}"
14+
EndProject
15+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ls-jdks", "tools\ls-jdks\ls-jdks.csproj", "{3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}"
16+
EndProject
1317
Global
1418
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1519
Debug|Any CPU = Debug|Any CPU
@@ -32,11 +36,18 @@ Global
3236
{4F5FD01C-B5A6-4F9F-91CE-3E78B3F942AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
3337
{4F5FD01C-B5A6-4F9F-91CE-3E78B3F942AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
3438
{4F5FD01C-B5A6-4F9F-91CE-3E78B3F942AC}.Release|Any CPU.Build.0 = Release|Any CPU
39+
{3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40+
{3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Debug|Any CPU.Build.0 = Debug|Any CPU
41+
{3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Release|Any CPU.ActiveCfg = Release|Any CPU
42+
{3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Release|Any CPU.Build.0 = Release|Any CPU
3543
EndGlobalSection
3644
GlobalSection(SolutionProperties) = preSolution
3745
HideSolutionNode = FALSE
3846
EndGlobalSection
3947
GlobalSection(ExtensibilityGlobals) = postSolution
4048
SolutionGuid = {BA1CD771-F00B-4DE8-93EE-7690D81F6A5A}
4149
EndGlobalSection
50+
GlobalSection(NestedProjects) = preSolution
51+
{3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D} = {19FE1B44-DB71-4F97-A07E-085888690DAE}
52+
EndGlobalSection
4253
EndGlobal

src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public static void DetectAndSetPreferredJavaSdkPathToLatest (Action<TraceLevel,
185185

186186
logger = logger ?? DefaultConsoleLogger;
187187

188-
var latestJdk = JdkInfo.GetMicrosoftOpenJdks (logger).FirstOrDefault ();
188+
var latestJdk = MicrosoftOpenJdkLocations.GetMicrosoftOpenJdks (logger).FirstOrDefault ();
189189
if (latestJdk == null)
190190
throw new NotSupportedException ("No Microsoft OpenJDK could be found. Please re-run the Visual Studio installer or manually specify the JDK path in settings.");
191191

src/Xamarin.Android.Tools.AndroidSdk/JdkInfo.cs

Lines changed: 7 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -285,78 +285,20 @@ public static IEnumerable<JdkInfo> GetKnownSystemJdkInfos (Action<TraceLevel, st
285285
logger = logger ?? AndroidSdkInfo.DefaultConsoleLogger;
286286

287287
return GetEnvironmentVariableJdks ("JI_JAVA_HOME", logger)
288-
.Concat (GetWindowsJdks (logger))
289-
.Concat (GetConfiguredJdks (logger))
290-
.Concat (GetMacOSMicrosoftOpenJdks (logger))
288+
.Concat (JdkLocations.GetPreferredJdks (logger))
289+
.Concat (XAPrepareJdkLocations.GetXAPrepareJdks (logger))
290+
.Concat (MicrosoftOpenJdkLocations.GetMicrosoftOpenJdks (logger))
291+
.Concat (AzulJdkLocations.GetAzulJdks (logger))
292+
.Concat (OracleJdkLocations.GetOracleJdks (logger))
293+
.Concat (VSAndroidJdkLocations.GetVSAndroidJdks (logger))
291294
.Concat (GetEnvironmentVariableJdks ("JAVA_HOME", logger))
292295
.Concat (GetPathEnvironmentJdks (logger))
293296
.Concat (GetLibexecJdks (logger))
294297
.Concat (GetJavaAlternativesJdks (logger))
295298
;
296299
}
297300

298-
static IEnumerable<JdkInfo> GetConfiguredJdks (Action<TraceLevel, string> logger)
299-
{
300-
return GetConfiguredJdkPaths (logger)
301-
.Select (p => TryGetJdkInfo (p, logger, "monodroid-config.xml"))
302-
.Where (jdk => jdk != null)
303-
.Select (jdk => jdk!)
304-
.OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default);
305-
}
306-
307-
static IEnumerable<string> GetConfiguredJdkPaths (Action<TraceLevel, string> logger)
308-
{
309-
var config = AndroidSdkUnix.GetUnixConfigFile (logger);
310-
foreach (var java_sdk in config.Root.Elements ("java-sdk")) {
311-
var path = (string) java_sdk.Attribute ("path");
312-
yield return path;
313-
}
314-
}
315-
316-
internal static IEnumerable<JdkInfo> GetMicrosoftOpenJdks (Action<TraceLevel, string> logger)
317-
{
318-
foreach (var dir in GetMacOSMicrosoftOpenJdks (logger))
319-
yield return dir;
320-
if (Path.DirectorySeparatorChar != '\\')
321-
yield break;
322-
foreach (var dir in AndroidSdkWindows.GetJdkInfos (logger)) {
323-
yield return dir;
324-
}
325-
}
326-
327-
static IEnumerable<JdkInfo> GetMacOSMicrosoftOpenJdks (Action<TraceLevel, string> logger)
328-
{
329-
return GetMacOSMicrosoftOpenJdkPaths ()
330-
.Select (p => TryGetJdkInfo (p, logger, "/Library/Java/JavaVirtualMachines/microsoft-*.jdk"))
331-
.Where (jdk => jdk != null)
332-
.Select (jdk => jdk!)
333-
.OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default);
334-
}
335-
336-
static IEnumerable<string> GetMacOSMicrosoftOpenJdkPaths ()
337-
{
338-
var root = "/Library/Java/JavaVirtualMachines";
339-
var pattern = "microsoft-*.jdk";
340-
var toHome = Path.Combine ("Contents", "Home");
341-
var jdks = AppDomain.CurrentDomain.GetData ($"GetMacOSMicrosoftJdkPaths jdks override! {typeof (JdkInfo).AssemblyQualifiedName}")
342-
?.ToString ();
343-
if (jdks != null) {
344-
root = jdks;
345-
toHome = "";
346-
pattern = "*";
347-
}
348-
if (!Directory.Exists (root)) {
349-
yield break;
350-
}
351-
foreach (var dir in Directory.EnumerateDirectories (root, pattern)) {
352-
var home = Path.Combine (dir, toHome);
353-
if (!Directory.Exists (home))
354-
continue;
355-
yield return home;
356-
}
357-
}
358-
359-
static JdkInfo? TryGetJdkInfo (string path, Action<TraceLevel, string> logger, string locator)
301+
internal static JdkInfo? TryGetJdkInfo (string path, Action<TraceLevel, string> logger, string locator)
360302
{
361303
JdkInfo? jdk = null;
362304
try {
@@ -369,13 +311,6 @@ static IEnumerable<string> GetMacOSMicrosoftOpenJdkPaths ()
369311
return jdk;
370312
}
371313

372-
static IEnumerable<JdkInfo> GetWindowsJdks (Action<TraceLevel, string> logger)
373-
{
374-
if (!OS.IsWindows)
375-
return Enumerable.Empty<JdkInfo> ();
376-
return AndroidSdkWindows.GetJdkInfos (logger);
377-
}
378-
379314
static IEnumerable<JdkInfo> GetEnvironmentVariableJdks (string envVar, Action<TraceLevel, string> logger)
380315
{
381316
var java_home = Environment.GetEnvironmentVariable (envVar);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
8+
namespace Xamarin.Android.Tools {
9+
10+
class AzulJdkLocations : JdkLocations {
11+
12+
internal static IEnumerable<JdkInfo> GetAzulJdks (Action<TraceLevel, string> logger)
13+
{
14+
return GetMacOSSystemJdks ("zulu-*.jdk", logger)
15+
.Concat (GetWindowsFileSystemJdks (Path.Combine ("Zulu", "zulu-*"), logger))
16+
.Concat (GetWindowsRegistryJdks (logger, @"SOFTWARE\Azul Systems\Zulu", "zulu-*", null, "InstallationPath"))
17+
.OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default);
18+
}
19+
}
20+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
7+
namespace Xamarin.Android.Tools {
8+
9+
partial class JdkLocations {
10+
11+
static IEnumerable<JdkInfo> GetUnixPreferredJdks (Action<TraceLevel, string> logger)
12+
{
13+
return GetUnixConfiguredJdkPaths (logger)
14+
.Select (p => JdkInfo.TryGetJdkInfo (p, logger, "monodroid-config.xml"))
15+
.Where (jdk => jdk != null)
16+
.Select (jdk => jdk!)
17+
.OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default);
18+
}
19+
20+
static IEnumerable<string> GetUnixConfiguredJdkPaths (Action<TraceLevel, string> logger)
21+
{
22+
var config = AndroidSdkUnix.GetUnixConfigFile (logger);
23+
foreach (var java_sdk in config.Root.Elements ("java-sdk")) {
24+
var path = (string) java_sdk.Attribute ("path");
25+
yield return path;
26+
}
27+
}
28+
29+
const string MacOSJavaVirtualMachinesRoot = "/Library/Java/JavaVirtualMachines";
30+
31+
protected static IEnumerable<JdkInfo> GetMacOSSystemJdks (string pattern, Action<TraceLevel, string> logger, string? locator = null)
32+
{
33+
if (!OS.IsMac) {
34+
return Array.Empty<JdkInfo>();
35+
}
36+
locator = locator ?? Path.Combine (MacOSJavaVirtualMachinesRoot, pattern);
37+
return FromPaths (GetMacOSSystemJvmPaths (pattern), logger, locator);
38+
}
39+
40+
static IEnumerable<string> GetMacOSSystemJvmPaths (string pattern)
41+
{
42+
var root = MacOSJavaVirtualMachinesRoot;
43+
var toHome = Path.Combine ("Contents", "Home");
44+
var jdks = AppDomain.CurrentDomain.GetData ($"GetMacOSMicrosoftJdkPaths jdks override! {typeof (JdkInfo).AssemblyQualifiedName}")
45+
?.ToString ();
46+
if (jdks != null) {
47+
root = jdks;
48+
toHome = "";
49+
pattern = "*";
50+
}
51+
if (!Directory.Exists (root)) {
52+
yield break;
53+
}
54+
foreach (var dir in Directory.EnumerateDirectories (root, pattern)) {
55+
var home = Path.Combine (dir, toHome);
56+
if (!Directory.Exists (home))
57+
continue;
58+
yield return home;
59+
}
60+
}
61+
}
62+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Text.RegularExpressions;
8+
9+
namespace Xamarin.Android.Tools {
10+
11+
partial class JdkLocations {
12+
13+
internal static string? GetWindowsPreferredJdkPath ()
14+
{
15+
var wow = RegistryEx.Wow64.Key32;
16+
var regKey = AndroidSdkWindows.GetMDRegistryKey ();
17+
if (RegistryEx.CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, regKey, AndroidSdkWindows.MDREG_JAVA_SDK, wow, "bin", "java.exe"))
18+
return RegistryEx.GetValueString (RegistryEx.CurrentUser, regKey, AndroidSdkWindows.MDREG_JAVA_SDK, wow);
19+
return null;
20+
21+
}
22+
23+
protected static IEnumerable<JdkInfo> GetWindowsFileSystemJdks (string pattern, Action<TraceLevel, string> logger, string? locator = null)
24+
{
25+
if (!OS.IsWindows) {
26+
yield break;
27+
}
28+
29+
var roots = new List<string>() {
30+
// "Compatibility" with existing codebases; should probably be avoided…
31+
Environment.ExpandEnvironmentVariables ("%ProgramW6432%"),
32+
Environment.GetFolderPath (Environment.SpecialFolder.ProgramFiles),
33+
Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86),
34+
};
35+
foreach (var root in roots) {
36+
if (!Directory.Exists (root))
37+
continue;
38+
IEnumerable<string> homes;
39+
try {
40+
homes = Directory.EnumerateDirectories (root, pattern);
41+
}
42+
catch (IOException e) {
43+
continue;
44+
}
45+
foreach (var home in homes) {
46+
if (!File.Exists (Path.Combine (home, "bin", "java.exe")))
47+
continue;
48+
var loc = locator ?? $"{pattern} via {root}";
49+
var jdk = JdkInfo.TryGetJdkInfo (home, logger, loc);
50+
if (jdk == null)
51+
continue;
52+
yield return jdk;
53+
}
54+
}
55+
}
56+
57+
protected static IEnumerable<JdkInfo> GetWindowsRegistryJdks (
58+
Action<TraceLevel, string> logger,
59+
string parentKey,
60+
string childKeyGlob,
61+
string? grandChildKey,
62+
string valueName,
63+
string? locator = null)
64+
{
65+
if (!OS.IsWindows) {
66+
yield break;
67+
}
68+
69+
var regex = ToRegex (childKeyGlob);
70+
var paths = new List<(Version version, string path)> ();
71+
var roots = new[] { RegistryEx.CurrentUser, RegistryEx.LocalMachine };
72+
var wows = new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 };
73+
foreach (var root in roots)
74+
foreach (var wow in wows) {
75+
foreach (var subkeyName in RegistryEx.EnumerateSubkeys (root, parentKey, wow)) {
76+
if (!regex.Match (subkeyName).Success) {
77+
continue;
78+
}
79+
var key = $@"{parentKey}\{subkeyName}" +
80+
(grandChildKey == null ? "" : "\\" + grandChildKey);
81+
var path = RegistryEx.GetValueString (root, key, valueName, wow);
82+
if (path == null) {
83+
continue;
84+
}
85+
var jdk = JdkInfo.TryGetJdkInfo (path, logger, locator ?? $"Windows Registry @ {parentKey}");
86+
if (jdk == null) {
87+
continue;
88+
}
89+
yield return jdk;
90+
}
91+
}
92+
}
93+
94+
static Regex ToRegex (string glob)
95+
{
96+
var r = new StringBuilder ();
97+
foreach (char c in glob) {
98+
switch (c) {
99+
case '*':
100+
r.Append (".*");
101+
break;
102+
case '?':
103+
r.Append (".");
104+
break;
105+
default:
106+
r.Append (Regex.Escape (c.ToString ()));
107+
break;
108+
}
109+
}
110+
return new Regex (r.ToString (), RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
111+
}
112+
}
113+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
7+
namespace Xamarin.Android.Tools {
8+
9+
partial class JdkLocations {
10+
11+
internal static IEnumerable<JdkInfo> GetPreferredJdks (Action<TraceLevel, string> logger)
12+
{
13+
if (OS.IsWindows) {
14+
var path = GetWindowsPreferredJdkPath ();
15+
var jdk = path == null ? null : JdkInfo.TryGetJdkInfo (path, logger, "Windows Registry");
16+
if (jdk != null)
17+
yield return jdk;
18+
}
19+
foreach (var jdk in GetUnixPreferredJdks (logger)) {
20+
yield return jdk;
21+
}
22+
}
23+
24+
protected static IEnumerable<JdkInfo> FromPaths (IEnumerable<string> paths, Action<TraceLevel, string> logger, string locator)
25+
{
26+
return paths
27+
.Select (p => JdkInfo.TryGetJdkInfo (p, logger, locator))
28+
.Where (jdk => jdk != null)
29+
.Select (jdk => jdk!);
30+
}
31+
}
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
8+
namespace Xamarin.Android.Tools {
9+
10+
class MicrosoftOpenJdkLocations : JdkLocations {
11+
12+
internal static IEnumerable<JdkInfo> GetMicrosoftOpenJdks (Action<TraceLevel, string> logger)
13+
{
14+
return GetMacOSSystemJdks ("microsoft-*.jdk", logger)
15+
.Concat (GetWindowsFileSystemJdks (Path.Combine ("Microsoft", "jdk-*"), logger))
16+
.Concat (GetWindowsRegistryJdks (logger, @"SOFTWARE\Microsoft\JDK", "*", @"hotspot\MSI", "Path"))
17+
.OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default);
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)