Skip to content

Commit ef95399

Browse files
committed
[Xamarin.Android.Build.Tasks] Prefer supported JDK versions
Context: https://devdiv.visualstudio.com/DevDiv/_releaseProgress?releaseId=1029208&_a=release-environment-extension&environmentId=5321844&extensionId=ms.vss-test-web.test-result-in-release-environment-editor-tab&runId=21424746&resultId=100001&paneView=attachments Context: dotnet/android-tools@237642c Context: dotnet/android-tools@dca30d9 Context: dotnet/android-tools@d92fc3e Context: dotnet/android-tools@2241489 xamarin/xamarin-android-tools was updated to check for JDK locations in a variety of locations, checking for these locations (1) *after* a "preferred" JDK location, but (2) *before* `$JAVA_HOME` and the Visual Studio-installed "microsoft_dist_openjdk_1.8.0.25" package. Consequently, when running on an environment in which multiple JDKs are installed, *and* an *unsupported version* of a JDK is present, it's possible for build failures to result, as Xamarin.Android attempted to use an unsupported version. This happened during one of our internal CI runs: Project templates should have zero errors Expected: <empty> But was: < " [Line]: 248 [Description]: Building with JDK version 14.0.2 is not supported. Please install JDK version 11.0. See https://aka.ms/xamarin/jdk9-errors (XA0030) [File]: Xamarin.Android.Legacy.targets This particular machine had AdoptOpenJDK 14 installed, in addition to other probed-for JDK locations, but because it was found first -- `JdkInfo.GetKnownSystemJdkInfos()` doesn't know or care about Xamarin.Android JDK supported versions, and we're trying to deprecate the "microsoft_dist_openjdk*" package, and thus it shouldn't be preferred *anyway*… -- the Xamarin.Android build attempted to use JDK 14, when JDK 14 isn't supported. This could happen because the `<ResolveSdks/>` task calls [`MonoAndroidHelper.RefreshAndroidSdk()`][0], which calls [the `AndroidSdkInfo` constructor][1], which calls [`AndroidSdkBase.Initialize()`][2], which calls [`AndroidSdkBase.GetJavaSdkPaths()`][3]. If `$(JavaSdkDirectory)` isn't set (or is invalid), and a preferred JDK isn't set or is invalid, then `AndoridSdkInfo` will implicitly call `JdkInfo.GetKnownSystemJdkInfos()`, which doesn't know anything about Xamarin.Android's JDK version requirements. Fix this scenario by improving the `<ResolveSdks/>` and `<ResolveJdkJvmPath/>` tasks to use `$(MinimumSupportedJavaVersion)` and `$(LatestSupportedJavaVersion)` to constrain which JDK we use, using the first "known system JDK" which is greater than or equal to `$(MinimumSupportedJavaVersion)` and less than or equal to `$(LatestSupportedJavaVersion)`. The `$(JavaSdkDirectory)` MSBuild property is still preferred, if set, followed by the "preferred JDK location", if set. It's only if neither of these locations is set that we'll check for all known system JDKs, at which point we now filter them. [0]: https://github.com/xamarin/xamarin-android/blob/51bb87603a6ecbcf8b74b4d1a529fbbe2feac01a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs#L91-L117 [1]: https://github.com/xamarin/xamarin-android-tools/blob/683f37508b56c76c24b3287a5687743438625341/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs#L13-L25 [2]: https://github.com/xamarin/xamarin-android-tools/blob/683f37508b56c76c24b3287a5687743438625341/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs#L66-L99 [3]: https://github.com/xamarin/xamarin-android-tools/blob/683f37508b56c76c24b3287a5687743438625341/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs#L245-L249
1 parent a321d74 commit ef95399

File tree

7 files changed

+127
-40
lines changed

7 files changed

+127
-40
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/ResolveJdkJvmPath.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ public class ResolveJdkJvmPath : AndroidTask
2020
[Output]
2121
public string JdkJvmPath { get; set; }
2222

23+
[Required]
24+
public string MinimumSupportedJavaVersion { get; set; }
25+
26+
[Required]
27+
public string LatestSupportedJavaVersion { get; set; }
28+
2329
public override bool RunTask ()
2430
{
2531
try {
@@ -54,12 +60,10 @@ string GetJvmPath ()
5460
return cached;
5561
}
5662

57-
JdkInfo info = null;
58-
try {
59-
info = new JdkInfo (JavaSdkPath);
60-
} catch {
61-
info = JdkInfo.GetKnownSystemJdkInfos (this.CreateTaskLogger ()).FirstOrDefault ();
62-
}
63+
var minVersion = Version.Parse (MinimumSupportedJavaVersion);
64+
var maxVersion = Version.Parse (LatestSupportedJavaVersion);
65+
66+
JdkInfo info = MonoAndroidHelper.GetJdkInfo (this.CreateTaskLogger (), JavaSdkPath, minVersion, maxVersion);
6367

6468
if (info == null)
6569
return null;

src/Xamarin.Android.Build.Tasks/Tasks/ResolveSdksTask.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ public class ResolveSdks : AndroidTask
4646

4747
public string CommandLineToolsVersion { get; set; }
4848

49+
[Required]
50+
public string MinimumSupportedJavaVersion { get; set; }
51+
52+
[Required]
53+
public string LatestSupportedJavaVersion { get; set; }
54+
4955
[Output]
5056
public string CommandLineToolsPath { get; set; }
5157

@@ -81,6 +87,11 @@ public override bool RunTask ()
8187
MonoAndroidLibPath = MonoAndroidHelper.GetOSLibPath () + Path.DirectorySeparatorChar;
8288
AndroidBinUtilsPath = MonoAndroidBinPath + "ndk" + Path.DirectorySeparatorChar;
8389

90+
var minVersion = Version.Parse (MinimumSupportedJavaVersion);
91+
var maxVersion = Version.Parse (LatestSupportedJavaVersion);
92+
93+
JavaSdkPath = MonoAndroidHelper.GetJdkInfo (this.CreateTaskLogger (), JavaSdkPath, minVersion, maxVersion)?.HomePath;
94+
8495
MonoAndroidHelper.RefreshSupportedVersions (ReferenceAssemblyPaths);
8596

8697
try {

src/Xamarin.Android.Build.Tasks/Tasks/ValidateJavaVersion.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ public override bool RunTask ()
4848
// `java -version` will produce values such as:
4949
// java version "9.0.4"
5050
// java version "1.8.0_77"
51-
static readonly Regex JavaVersionRegex = new Regex (@"version ""(?<version>[\d\.]+)(_d+)?[^""]*""");
51+
static readonly Regex JavaVersionRegex = new Regex (@"version ""(?<version>[\d\.]+)(_\d+)?[^""]*""");
5252

5353
// `javac -version` will produce values such as:
5454
// javac 9.0.4
5555
// javac 1.8.0_77
56-
static readonly Regex JavacVersionRegex = new Regex (@"(?<version>[\d\.]+)(_d+)?");
56+
internal static readonly Regex JavacVersionRegex = new Regex (@"(?<version>[\d\.]+)(_\d+)?");
5757

5858
bool ValidateJava ()
5959
{

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/ResolveSdksTaskTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public class ResolveSdksTaskTests : BaseTest {
2525
new ApiInfo () { Id = "Z", Level = 127, Name = "Z", FrameworkVersion = "v108.1.99", Stable = false },
2626
};
2727

28+
// via Xamarin.Android.Common.props
29+
const string MinimumSupportedJavaVersion = "1.6.0";
30+
const string LatestSupportedJavaVersion = "11.0.99";
31+
2832
static object [] UseLatestAndroidSdkTestCases = new object [] {
2933
new object[] {
3034
/* buildtools */ "26.0.3",
@@ -167,6 +171,8 @@ public void UseLatestAndroidSdk (string buildtools, string jdk, ApiInfo[] apis,
167171
AndroidSdkPath = androidSdkPath,
168172
AndroidNdkPath = androidNdkPath,
169173
JavaSdkPath = javaPath,
174+
MinimumSupportedJavaVersion = MinimumSupportedJavaVersion,
175+
LatestSupportedJavaVersion = LatestSupportedJavaVersion,
170176
ReferenceAssemblyPaths = new [] {
171177
Path.Combine (referencePath, "MonoAndroid"),
172178
},
@@ -220,6 +226,8 @@ public void ResolveSdkTiming ()
220226
AndroidSdkPath = androidSdkPath,
221227
AndroidNdkPath = androidNdkPath,
222228
JavaSdkPath = javaPath,
229+
MinimumSupportedJavaVersion = MinimumSupportedJavaVersion,
230+
LatestSupportedJavaVersion = LatestSupportedJavaVersion,
223231
ReferenceAssemblyPaths = new [] {
224232
Path.Combine (referencePath, "MonoAndroid"),
225233
},
@@ -391,6 +399,8 @@ public void TargetFrameworkPairing (string description, ApiInfo[] androidSdk, Ap
391399
AndroidSdkPath = androidSdkPath,
392400
AndroidNdkPath = androidNdkPath,
393401
JavaSdkPath = javaPath,
402+
MinimumSupportedJavaVersion = MinimumSupportedJavaVersion,
403+
LatestSupportedJavaVersion = LatestSupportedJavaVersion,
394404
ReferenceAssemblyPaths = new [] {
395405
Path.Combine (referencePath, "MonoAndroid"),
396406
},

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -389,48 +389,93 @@ protected string CreateFauxReferencesDirectory (string path, ApiInfo [] versions
389389

390390
protected string CreateFauxJavaSdkDirectory (string path, string javaVersion, out string javaExe, out string javacExe)
391391
{
392-
javaExe = IsWindows ? "Java.cmd" : "java.bash";
393-
javacExe = IsWindows ? "Javac.cmd" : "javac.bash";
394-
var jarSigner = IsWindows ? "jarsigner.exe" : "jarsigner";
392+
javaExe = IsWindows ? "java.cmd" : "java";
393+
javacExe = IsWindows ? "javac.cmd" : "javac";
394+
395395
var javaPath = Path.Combine (Root, path);
396-
var javaBinPath = Path.Combine (javaPath, "bin");
397-
Directory.CreateDirectory (javaBinPath);
398396

399-
CreateFauxJavaExe (Path.Combine (javaBinPath, javaExe), javaVersion);
400-
CreateFauxJavacExe (Path.Combine (javaBinPath, javacExe), javaVersion);
397+
CreateFauxJdk (javaPath, javaVersion, javaVersion, javaVersion);
401398

399+
var jarSigner = IsWindows ? "jarsigner.exe" : "jarsigner";
400+
var javaBinPath = Path.Combine (javaPath, "bin");
402401
File.WriteAllText (Path.Combine (javaBinPath, jarSigner), "");
402+
403403
return javaPath;
404404
}
405405

406-
void CreateFauxJavaExe (string javaExeFullPath, string version)
406+
// https://github.com/xamarin/xamarin-android-tools/blob/683f37508b56c76c24b3287a5687743438625341/tests/Xamarin.Android.Tools.AndroidSdk-Tests/JdkInfoTests.cs#L60-L100
407+
void CreateFauxJdk (string dir, string releaseVersion, string releaseBuildNumber, string javaVersion)
407408
{
408-
var sb = new StringBuilder ();
409-
if (IsWindows) {
410-
sb.AppendLine ("@echo off");
411-
sb.AppendLine ($"echo java version \"{version}\"");
412-
sb.AppendLine ($"echo Java(TM) SE Runtime Environment (build {version}-b13)");
413-
sb.AppendLine ($"echo Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)");
414-
} else {
415-
sb.AppendLine ("#!/bin/bash");
416-
sb.AppendLine ($"echo \"java version \\\"{version}\\\"\"");
417-
sb.AppendLine ($"echo \"Java(TM) SE Runtime Environment (build {version}-b13)\"");
418-
sb.AppendLine ($"echo \"Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)\"");
419-
}
420-
CreateFauxExecutable (javaExeFullPath, sb);
421-
}
422-
423-
void CreateFauxJavacExe (string javacExeFullPath, string version)
409+
Directory.CreateDirectory (dir);
410+
411+
using (var release = new StreamWriter (Path.Combine (dir, "release"))) {
412+
release.WriteLine ($"JAVA_VERSION=\"{releaseVersion}\"");
413+
release.WriteLine ($"BUILD_NUMBER={releaseBuildNumber}");
414+
release.WriteLine ($"JUST_A_KEY");
415+
}
416+
417+
var bin = Path.Combine (dir, "bin");
418+
var inc = Path.Combine (dir, "include");
419+
var jre = Path.Combine (dir, "jre");
420+
var jli = Path.Combine (jre, "lib", "jli");
421+
422+
Directory.CreateDirectory (bin);
423+
Directory.CreateDirectory (inc);
424+
Directory.CreateDirectory (jli);
425+
Directory.CreateDirectory (jre);
426+
427+
string quote = IsWindows ? "" : "\"";
428+
string java = IsWindows
429+
? $"echo java version \"{javaVersion}\"{Environment.NewLine}"
430+
: $"echo java version '\"{javaVersion}\"'{Environment.NewLine}";
431+
java = java +
432+
$"echo Property settings:{Environment.NewLine}" +
433+
$"echo {quote} java.home = {dir}{quote}{Environment.NewLine}" +
434+
$"echo {quote} java.vendor = Xamarin.Android Unit Tests{quote}{Environment.NewLine}" +
435+
$"echo {quote} java.version = {javaVersion}{quote}{Environment.NewLine}" +
436+
$"echo {quote} xamarin.multi-line = line the first{quote}{Environment.NewLine}" +
437+
$"echo {quote} line the second{quote}{Environment.NewLine}" +
438+
$"echo {quote} .{quote}{Environment.NewLine}";
439+
440+
string javac =
441+
$"echo javac {javaVersion}{Environment.NewLine}";
442+
443+
CreateShellScript (Path.Combine (bin, "jar"), "");
444+
CreateShellScript (Path.Combine (bin, "java"), java);
445+
CreateShellScript (Path.Combine (bin, "javac"), javac);
446+
CreateShellScript (Path.Combine (jli, "libjli.dylib"), "");
447+
CreateShellScript (Path.Combine (jre, "libjvm.so"), "");
448+
CreateShellScript (Path.Combine (jre, "jvm.dll"), "");
449+
}
450+
451+
// https://github.com/xamarin/xamarin-android-tools/blob/683f37508b56c76c24b3287a5687743438625341/tests/Xamarin.Android.Tools.AndroidSdk-Tests/JdkInfoTests.cs#L108-L132
452+
void CreateShellScript (string path, string contents)
424453
{
425-
var sb = new StringBuilder ();
426-
if (IsWindows) {
427-
sb.AppendLine ("@echo off");
428-
sb.AppendLine ($"echo javac {version}");
429-
} else {
430-
sb.AppendLine ("#!/bin/bash");
431-
sb.AppendLine ($"echo \"javac {version}\"");
454+
if (IsWindows && string.Compare (Path.GetExtension (path), ".dll", true) != 0)
455+
path += ".cmd";
456+
using (var script = new StreamWriter (path)) {
457+
if (IsWindows) {
458+
script.WriteLine ("@echo off");
459+
}
460+
else {
461+
script.WriteLine ("#!/bin/sh");
462+
}
463+
script.WriteLine (contents);
432464
}
433-
CreateFauxExecutable (javacExeFullPath, sb);
465+
if (IsWindows)
466+
return;
467+
var chmod = new ProcessStartInfo {
468+
FileName = "chmod",
469+
Arguments = $"+x \"{path}\"",
470+
UseShellExecute = false,
471+
RedirectStandardInput = false,
472+
RedirectStandardOutput = true,
473+
RedirectStandardError = true,
474+
CreateNoWindow = true,
475+
WindowStyle = ProcessWindowStyle.Hidden,
476+
};
477+
var p = Process.Start (chmod);
478+
p.WaitForExit ();
434479
}
435480

436481
void CreateFauxExecutable (string exeFullPath, StringBuilder sb) {

src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,19 @@ public static void RefreshSupportedVersions (string[] referenceAssemblyPaths)
122122
}
123123
#endif // MSBUILD
124124

125+
public static JdkInfo GetJdkInfo (Action<TraceLevel, string> logger, string javaSdkPath, Version minSupportedVersion, Version maxSupportedVersion)
126+
{
127+
JdkInfo info = null;
128+
try {
129+
info = new JdkInfo (javaSdkPath);
130+
} catch {
131+
info = JdkInfo.GetKnownSystemJdkInfos (logger)
132+
.Where (jdk => jdk.Version >= minSupportedVersion && jdk.Version <= maxSupportedVersion)
133+
.FirstOrDefault ();
134+
}
135+
return info;
136+
}
137+
125138
class SizeAndContentFileComparer : IEqualityComparer<FileInfo>
126139
#if MSBUILD
127140
, IEqualityComparer<ITaskItem>

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ projects.
6767
AndroidSdkPath="$(AndroidSdkDirectory)"
6868
AndroidNdkPath="$(AndroidNdkDirectory)"
6969
JavaSdkPath="$(JavaSdkDirectory)"
70+
MinimumSupportedJavaVersion="$(MinimumSupportedJavaVersion)"
71+
LatestSupportedJavaVersion="$(LatestSupportedJavaVersion)"
7072
ReferenceAssemblyPaths="$(_XATargetFrameworkDirectories)">
7173
<Output TaskParameter="CommandLineToolsPath" PropertyName="_AndroidToolsDirectory" />
7274
<Output TaskParameter="AndroidNdkPath" PropertyName="AndroidNdkDirectory" Condition=" '$(AndroidNdkDirectory)' == '' " />
@@ -82,6 +84,8 @@ projects.
8284
</ResolveSdks>
8385
<ResolveJdkJvmPath
8486
JavaSdkPath="$(_JavaSdkDirectory)"
87+
MinimumSupportedJavaVersion="$(MinimumSupportedJavaVersion)"
88+
LatestSupportedJavaVersion="$(LatestSupportedJavaVersion)"
8589
Condition=" '$(DesignTimeBuild)' != 'True' And '$(_AndroidIsBindingProject)' != 'True' And '$(AndroidGenerateJniMarshalMethods)' == 'True' And '$(JdkJvmPath)' == '' ">
8690
<Output TaskParameter="JdkJvmPath" PropertyName="JdkJvmPath" />
8791
</ResolveJdkJvmPath>

0 commit comments

Comments
 (0)