Skip to content

Commit 13cc497

Browse files
authored
[Xamarin.Android.Tools.AndroidSdk] Prefer JAVA_HOME (#86)
Context: dotnet/android#4567 `JdkInfo.GetKnownSystemJdkInfos()` returns a list of "known JDK locations", and the order of the returned paths was, basically, "locations that Xamarin/Microsoft controls come first." `AndroidSdkWindows.GetJdkInfos()` reads the Windows registry and is "controlled by" Visual Studio (unless someone is editing the Registry by hand…). If not on Windows, `GetConfiguredJdks()` reads `monodroid-config.xml` (managed by Visual Studio for Mac); failing that we probe some "well known Microsoft-controlled" directory locations… …and only failing *that* do we try the `$JAVA_HOME` environment variable, the ["de-facto" way][0] to tell software where Java is installed, and if `$JAVA_HOME` isn't set we further fallback to checking directories in `$PATH` and other mechanisms. The problem with this approach is that it isn't overridable, which is a usefully important feature if you want to *test new JDK versions*, as is the case in dotnet/android#4567. The "obvious" way to "try out" a new JDK would be to export the `JAVA_HOME` environment variable to the location of the JDK to use, but *that won't work* because `JdkInfo.GetKnownSystemJdkInfos()` *explicitly prefers* locations that aren't easily controllable in a CI environment. Given that *existing convention* is for JDK installs to set the `JAVA_HOME` environment variable -- and thus `JAVA_HOME` may very well refer to a JDK which Xamarin.Android doesn't support -- we are leery of making `JAVA_HOME` the "primary" override. Instead, add support for a new `JI_JAVA_HOME` environment variable which, if set, is the *preferred* JDK to use within Xamarin.Android (unless otherwise overridden by e.g. `$(JavaSdkDirectory)`). This will allow CI to export the `JAVA_HOME` environment variable, allowing it to be preferred over others. Additionally, remove some `JAVA_HOME` "duplication" between `JdkInfo` and `AndroidSdkWindows`, so that things are easier to reason about. [0]: https://docs.oracle.com/cd/E19182-01/821-0917/inst_jdk_javahome_t/index.html
1 parent 967c278 commit 13cc497

File tree

4 files changed

+34
-24
lines changed

4 files changed

+34
-24
lines changed

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

+6-5
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,11 @@ public static IEnumerable<JdkInfo> GetKnownSystemJdkInfos (Action<TraceLevel, st
281281
{
282282
logger = logger ?? AndroidSdkInfo.DefaultConsoleLogger;
283283

284-
return GetWindowsJdks (logger)
284+
return GetEnvironmentVariableJdks ("JI_JAVA_HOME", logger)
285+
.Concat (GetWindowsJdks (logger))
285286
.Concat (GetConfiguredJdks (logger))
286287
.Concat (GetMacOSMicrosoftJdks (logger))
287-
.Concat (GetJavaHomeEnvironmentJdks (logger))
288+
.Concat (GetEnvironmentVariableJdks ("JAVA_HOME", logger))
288289
.Concat (GetPathEnvironmentJdks (logger))
289290
.Concat (GetLibexecJdks (logger))
290291
.Concat (GetJavaAlternativesJdks (logger))
@@ -352,12 +353,12 @@ static IEnumerable<JdkInfo> GetWindowsJdks (Action<TraceLevel, string> logger)
352353
return AndroidSdkWindows.GetJdkInfos (logger);
353354
}
354355

355-
static IEnumerable<JdkInfo> GetJavaHomeEnvironmentJdks (Action<TraceLevel, string> logger)
356+
static IEnumerable<JdkInfo> GetEnvironmentVariableJdks (string envVar, Action<TraceLevel, string> logger)
356357
{
357-
var java_home = Environment.GetEnvironmentVariable ("JAVA_HOME");
358+
var java_home = Environment.GetEnvironmentVariable (envVar);
358359
if (string.IsNullOrEmpty (java_home))
359360
yield break;
360-
var jdk = TryGetJdkInfo (java_home, logger, "$JAVA_HOME");
361+
var jdk = TryGetJdkInfo (java_home, logger, $"${envVar}");
361362
if (jdk != null)
362363
yield return jdk;
363364
}

src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs

+2-13
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ protected override IEnumerable<string> GetAllAvailableAndroidSdks ()
108108

109109
protected override string? GetJavaSdkPath ()
110110
{
111-
var jdk = GetJdkInfos (Logger).FirstOrDefault ();
111+
var jdk = JdkInfo.GetKnownSystemJdkInfos (Logger).FirstOrDefault ();
112112
return jdk?.HomePath;
113113
}
114114

@@ -139,18 +139,7 @@ IEnumerable<JdkInfo> ToJdkInfos (IEnumerable<string> paths, string locator)
139139
.Concat (ToJdkInfos (GetOpenJdkPaths (), "OpenJDK"))
140140
.Concat (ToJdkInfos (GetKnownOpenJdkPaths (), "Well-known OpenJDK paths"))
141141
.Concat (ToJdkInfos (GetOracleJdkPaths (), "Oracle JDK"))
142-
.Concat (ToJdkInfos (GetEnvironmentJdkPaths (), "Environment Variables"));
143-
}
144-
145-
private static IEnumerable<string> GetEnvironmentJdkPaths ()
146-
{
147-
var environment = new [] { "JAVA_HOME" };
148-
foreach (var key in environment) {
149-
var value = Environment.GetEnvironmentVariable (key);
150-
if (!string.IsNullOrEmpty (value)) {
151-
yield return value;
152-
}
153-
}
142+
;
154143
}
155144

156145
private static IEnumerable<string> GetPreferredJdkPaths ()

tests/Xamarin.Android.Tools.AndroidSdk-Tests/AndroidSdkInfoTests.cs

+9-6
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,13 @@ public void Constructor_SetValuesFromPath ()
136136
}
137137

138138
[Test]
139-
[Ignore ("This test will only work locally if you rename/remove your Open JDK directory.")]
140-
public void JdkDirectory_JavaHome ()
139+
public void JdkDirectory_JavaHome ([Values ("JI_JAVA_HOME", "JAVA_HOME")] string envVar)
141140
{
141+
if (envVar.Equals ("JAVA_HOME", StringComparison.OrdinalIgnoreCase)) {
142+
Assert.Ignore ("This test will only work locally if you rename/remove your Open JDK directory.");
143+
return;
144+
}
145+
142146
CreateSdks (out string root, out string jdk, out string ndk, out string sdk);
143147
JdkInfoTests.CreateFauxJdk (jdk, releaseVersion: "1.8.999", releaseBuildNumber: "9", javaVersion: "1.8.999-9");
144148

@@ -150,16 +154,15 @@ public void JdkDirectory_JavaHome ()
150154
string java_home = null;
151155
try {
152156
// We only set via JAVA_HOME
153-
java_home = Environment.GetEnvironmentVariable ("JAVA_HOME", EnvironmentVariableTarget.Process);
154-
Environment.SetEnvironmentVariable ("JAVA_HOME", jdk);
157+
java_home = Environment.GetEnvironmentVariable (envVar, EnvironmentVariableTarget.Process);
158+
Environment.SetEnvironmentVariable (envVar, jdk, EnvironmentVariableTarget.Process);
155159
var info = new AndroidSdkInfo (logger, androidSdkPath: sdk, androidNdkPath: ndk, javaSdkPath: "");
156160

157161
Assert.AreEqual (ndk, info.AndroidNdkPath, "AndroidNdkPath not preserved!");
158162
Assert.AreEqual (sdk, info.AndroidSdkPath, "AndroidSdkPath not preserved!");
159163
Assert.AreEqual (jdk, info.JavaSdkPath, "JavaSdkPath not preserved!");
160164
} finally {
161-
if (java_home != null)
162-
Environment.SetEnvironmentVariable ("JAVA_HOME", java_home, EnvironmentVariableTarget.Process);
165+
Environment.SetEnvironmentVariable (envVar, java_home, EnvironmentVariableTarget.Process);
163166
Directory.Delete (root, recursive: true);
164167
}
165168
}

tests/Xamarin.Android.Tools.AndroidSdk-Tests/JdkInfoTests.cs

+17
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,23 @@ namespace Xamarin.Android.Tools.Tests
1111
[TestFixture]
1212
public class JdkInfoTests
1313
{
14+
[Test]
15+
public void GetKnownSystemJdkInfos_PrefersJiJavaHome ()
16+
{
17+
var previous = Environment.GetEnvironmentVariable ("JI_JAVA_HOME", EnvironmentVariableTarget.Process);
18+
try {
19+
Environment.SetEnvironmentVariable ("JI_JAVA_HOME", FauxJdkDir, EnvironmentVariableTarget.Process);
20+
21+
var defaultJdkDir = JdkInfo.GetKnownSystemJdkInfos ()
22+
.FirstOrDefault ();
23+
Assert.IsNotNull (defaultJdkDir);
24+
Assert.AreEqual (FauxJdkDir, defaultJdkDir.HomePath);
25+
}
26+
finally {
27+
Environment.SetEnvironmentVariable ("JI_JAVA_HOME", previous, EnvironmentVariableTarget.Process);
28+
}
29+
}
30+
1431
[Test]
1532
public void Constructor_NullPath ()
1633
{

0 commit comments

Comments
 (0)