Skip to content
This repository was archived by the owner on Jul 13, 2026. It is now read-only.

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

17 files changed

Lines changed: 430 additions & 310 deletions

‎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-
varlatestJdk=JdkInfo.GetMicrosoftOpenJdks(logger).FirstOrDefault();
188+
varlatestJdk=MicrosoftOpenJdkLocations.GetMicrosoftOpenJdks(logger).FirstOrDefault();
189189
if(latestJdk==null)
190190
thrownewNotSupportedException("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
returnGetEnvironmentVariableJdks("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-
staticIEnumerable<JdkInfo>GetConfiguredJdks(Action<TraceLevel,string>logger)
299-
{
300-
returnGetConfiguredJdkPaths(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-
staticIEnumerable<string>GetConfiguredJdkPaths(Action<TraceLevel,string>logger)
308-
{
309-
varconfig=AndroidSdkUnix.GetUnixConfigFile(logger);
310-
foreach(varjava_sdkinconfig.Root.Elements("java-sdk")){
311-
varpath=(string)java_sdk.Attribute("path");
312-
yieldreturnpath;
313-
}
314-
}
315-
316-
internalstaticIEnumerable<JdkInfo>GetMicrosoftOpenJdks(Action<TraceLevel,string>logger)
317-
{
318-
foreach(vardirinGetMacOSMicrosoftOpenJdks(logger))
319-
yieldreturndir;
320-
if(Path.DirectorySeparatorChar!='\\')
321-
yieldbreak;
322-
foreach(vardirinAndroidSdkWindows.GetJdkInfos(logger)){
323-
yieldreturndir;
324-
}
325-
}
326-
327-
staticIEnumerable<JdkInfo>GetMacOSMicrosoftOpenJdks(Action<TraceLevel,string>logger)
328-
{
329-
returnGetMacOSMicrosoftOpenJdkPaths()
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-
staticIEnumerable<string>GetMacOSMicrosoftOpenJdkPaths()
337-
{
338-
varroot="/Library/Java/JavaVirtualMachines";
339-
varpattern="microsoft-*.jdk";
340-
vartoHome=Path.Combine("Contents","Home");
341-
varjdks=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-
yieldbreak;
350-
}
351-
foreach(vardirinDirectory.EnumerateDirectories(root,pattern)){
352-
varhome=Path.Combine(dir,toHome);
353-
if(!Directory.Exists(home))
354-
continue;
355-
yieldreturnhome;
356-
}
357-
}
358-
359-
staticJdkInfo?TryGetJdkInfo(stringpath,Action<TraceLevel,string>logger,stringlocator)
301+
internalstaticJdkInfo?TryGetJdkInfo(stringpath,Action<TraceLevel,string>logger,stringlocator)
360302
{
361303
JdkInfo?jdk=null;
362304
try{
@@ -369,13 +311,6 @@ static IEnumerable<string> GetMacOSMicrosoftOpenJdkPaths ()
369311
returnjdk;
370312
}
371313

372-
staticIEnumerable<JdkInfo>GetWindowsJdks(Action<TraceLevel,string>logger)
373-
{
374-
if(!OS.IsWindows)
375-
returnEnumerable.Empty<JdkInfo>();
376-
returnAndroidSdkWindows.GetJdkInfos(logger);
377-
}
378-
379314
staticIEnumerable<JdkInfo>GetEnvironmentVariableJdks(stringenvVar,Action<TraceLevel,string>logger)
380315
{
381316
varjava_home=Environment.GetEnvironmentVariable(envVar);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Diagnostics;
4+
usingSystem.IO;
5+
usingSystem.Linq;
6+
usingSystem.Text;
7+
8+
namespaceXamarin.Android.Tools{
9+
10+
classAzulJdkLocations:JdkLocations{
11+
12+
internalstaticIEnumerable<JdkInfo>GetAzulJdks(Action<TraceLevel,string>logger)
13+
{
14+
returnGetMacOSSystemJdks("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+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Diagnostics;
4+
usingSystem.IO;
5+
usingSystem.Linq;
6+
7+
namespaceXamarin.Android.Tools{
8+
9+
partialclassJdkLocations{
10+
11+
staticIEnumerable<JdkInfo>GetUnixPreferredJdks(Action<TraceLevel,string>logger)
12+
{
13+
returnGetUnixConfiguredJdkPaths(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+
staticIEnumerable<string>GetUnixConfiguredJdkPaths(Action<TraceLevel,string>logger)
21+
{
22+
varconfig=AndroidSdkUnix.GetUnixConfigFile(logger);
23+
foreach(varjava_sdkinconfig.Root.Elements("java-sdk")){
24+
varpath=(string)java_sdk.Attribute("path");
25+
yieldreturnpath;
26+
}
27+
}
28+
29+
conststringMacOSJavaVirtualMachinesRoot="/Library/Java/JavaVirtualMachines";
30+
31+
protectedstaticIEnumerable<JdkInfo>GetMacOSSystemJdks(stringpattern,Action<TraceLevel,string>logger,string?locator=null)
32+
{
33+
if(!OS.IsMac){
34+
returnArray.Empty<JdkInfo>();
35+
}
36+
locator=locator??Path.Combine(MacOSJavaVirtualMachinesRoot,pattern);
37+
returnFromPaths(GetMacOSSystemJvmPaths(pattern),logger,locator);
38+
}
39+
40+
staticIEnumerable<string>GetMacOSSystemJvmPaths(stringpattern)
41+
{
42+
varroot=MacOSJavaVirtualMachinesRoot;
43+
vartoHome=Path.Combine("Contents","Home");
44+
varjdks=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+
yieldbreak;
53+
}
54+
foreach(vardirinDirectory.EnumerateDirectories(root,pattern)){
55+
varhome=Path.Combine(dir,toHome);
56+
if(!Directory.Exists(home))
57+
continue;
58+
yieldreturnhome;
59+
}
60+
}
61+
}
62+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Diagnostics;
4+
usingSystem.IO;
5+
usingSystem.Linq;
6+
usingSystem.Text;
7+
usingSystem.Text.RegularExpressions;
8+
9+
namespaceXamarin.Android.Tools{
10+
11+
partialclassJdkLocations{
12+
13+
internalstaticstring?GetWindowsPreferredJdkPath()
14+
{
15+
varwow=RegistryEx.Wow64.Key32;
16+
varregKey=AndroidSdkWindows.GetMDRegistryKey();
17+
if(RegistryEx.CheckRegistryKeyForExecutable(RegistryEx.CurrentUser,regKey,AndroidSdkWindows.MDREG_JAVA_SDK,wow,"bin","java.exe"))
18+
returnRegistryEx.GetValueString(RegistryEx.CurrentUser,regKey,AndroidSdkWindows.MDREG_JAVA_SDK,wow);
19+
returnnull;
20+
21+
}
22+
23+
protectedstaticIEnumerable<JdkInfo>GetWindowsFileSystemJdks(stringpattern,Action<TraceLevel,string>logger,string?locator=null)
24+
{
25+
if(!OS.IsWindows){
26+
yieldbreak;
27+
}
28+
29+
varroots=newList<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(varrootinroots){
36+
if(!Directory.Exists(root))
37+
continue;
38+
IEnumerable<string>homes;
39+
try{
40+
homes=Directory.EnumerateDirectories(root,pattern);
41+
}
42+
catch(IOExceptione){
43+
continue;
44+
}
45+
foreach(varhomeinhomes){
46+
if(!File.Exists(Path.Combine(home,"bin","java.exe")))
47+
continue;
48+
varloc=locator??$"{pattern} via {root}";
49+
varjdk=JdkInfo.TryGetJdkInfo(home,logger,loc);
50+
if(jdk==null)
51+
continue;
52+
yieldreturnjdk;
53+
}
54+
}
55+
}
56+
57+
protectedstaticIEnumerable<JdkInfo>GetWindowsRegistryJdks(
58+
Action<TraceLevel,string>logger,
59+
stringparentKey,
60+
stringchildKeyGlob,
61+
string?grandChildKey,
62+
stringvalueName,
63+
string?locator=null)
64+
{
65+
if(!OS.IsWindows){
66+
yieldbreak;
67+
}
68+
69+
varregex=ToRegex(childKeyGlob);
70+
varpaths=newList<(Versionversion,stringpath)>();
71+
varroots=new[]{RegistryEx.CurrentUser,RegistryEx.LocalMachine};
72+
varwows=new[]{RegistryEx.Wow64.Key32,RegistryEx.Wow64.Key64};
73+
foreach(varrootinroots)
74+
foreach(varwowinwows){
75+
foreach(varsubkeyNameinRegistryEx.EnumerateSubkeys(root,parentKey,wow)){
76+
if(!regex.Match(subkeyName).Success){
77+
continue;
78+
}
79+
varkey=$@"{parentKey}\{subkeyName}"+
80+
(grandChildKey==null?"":"\\"+grandChildKey);
81+
varpath=RegistryEx.GetValueString(root,key,valueName,wow);
82+
if(path==null){
83+
continue;
84+
}
85+
varjdk=JdkInfo.TryGetJdkInfo(path,logger,locator??$"Windows Registry @ {parentKey}");
86+
if(jdk==null){
87+
continue;
88+
}
89+
yieldreturnjdk;
90+
}
91+
}
92+
}
93+
94+
staticRegexToRegex(stringglob)
95+
{
96+
varr=newStringBuilder();
97+
foreach(charcinglob){
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+
returnnewRegex(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+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Diagnostics;
4+
usingSystem.IO;
5+
usingSystem.Linq;
6+
7+
namespaceXamarin.Android.Tools{
8+
9+
partialclassJdkLocations{
10+
11+
internalstaticIEnumerable<JdkInfo>GetPreferredJdks(Action<TraceLevel,string>logger)
12+
{
13+
if(OS.IsWindows){
14+
varpath=GetWindowsPreferredJdkPath();
15+
varjdk=path==null?null:JdkInfo.TryGetJdkInfo(path,logger,"Windows Registry");
16+
if(jdk!=null)
17+
yieldreturnjdk;
18+
}
19+
foreach(varjdkinGetUnixPreferredJdks(logger)){
20+
yieldreturnjdk;
21+
}
22+
}
23+
24+
protectedstaticIEnumerable<JdkInfo>FromPaths(IEnumerable<string>paths,Action<TraceLevel,string>logger,stringlocator)
25+
{
26+
returnpaths
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+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Diagnostics;
4+
usingSystem.IO;
5+
usingSystem.Linq;
6+
usingSystem.Text;
7+
8+
namespaceXamarin.Android.Tools{
9+
10+
classMicrosoftOpenJdkLocations:JdkLocations{
11+
12+
internalstaticIEnumerable<JdkInfo>GetMicrosoftOpenJdks(Action<TraceLevel,string>logger)
13+
{
14+
returnGetMacOSSystemJdks("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)