From ef95399e157ba2eb5b51b1ab74737d45a43e6216 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 6 May 2021 12:57:07 -0400 Subject: [PATCH] [Xamarin.Android.Build.Tasks] Prefer supported JDK versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: https://github.com/xamarin/xamarin-android-tools/commit/237642cee7f95e37e87b26231b47c74796166594 Context: https://github.com/xamarin/xamarin-android-tools/commit/dca30d9d3aae6c2a209349ea94d6b42443e0d906 Context: https://github.com/xamarin/xamarin-android-tools/commit/d92fc3e3a27e8240551baa813b15d6bf006a5620 Context: https://github.com/xamarin/xamarin-android-tools/commit/2241489b31a985bfb6249b63ab93eb3746891aac 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: 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 `` 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 `` and `` 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 --- .../Tasks/ResolveJdkJvmPath.cs | 16 ++- .../Tasks/ResolveSdksTask.cs | 11 ++ .../Tasks/ValidateJavaVersion.cs | 4 +- .../Tasks/ResolveSdksTaskTests.cs | 10 ++ .../Utilities/BaseTest.cs | 109 +++++++++++++----- .../Utilities/MonoAndroidHelper.cs | 13 +++ .../Xamarin.Android.Tooling.targets | 4 + 7 files changed, 127 insertions(+), 40 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveJdkJvmPath.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveJdkJvmPath.cs index f4a895f6484..8695693ca35 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveJdkJvmPath.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveJdkJvmPath.cs @@ -20,6 +20,12 @@ public class ResolveJdkJvmPath : AndroidTask [Output] public string JdkJvmPath { get; set; } + [Required] + public string MinimumSupportedJavaVersion { get; set; } + + [Required] + public string LatestSupportedJavaVersion { get; set; } + public override bool RunTask () { try { @@ -54,12 +60,10 @@ string GetJvmPath () return cached; } - JdkInfo info = null; - try { - info = new JdkInfo (JavaSdkPath); - } catch { - info = JdkInfo.GetKnownSystemJdkInfos (this.CreateTaskLogger ()).FirstOrDefault (); - } + var minVersion = Version.Parse (MinimumSupportedJavaVersion); + var maxVersion = Version.Parse (LatestSupportedJavaVersion); + + JdkInfo info = MonoAndroidHelper.GetJdkInfo (this.CreateTaskLogger (), JavaSdkPath, minVersion, maxVersion); if (info == null) return null; diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveSdksTask.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveSdksTask.cs index 119ac921a23..d3096519471 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ResolveSdksTask.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ResolveSdksTask.cs @@ -46,6 +46,12 @@ public class ResolveSdks : AndroidTask public string CommandLineToolsVersion { get; set; } + [Required] + public string MinimumSupportedJavaVersion { get; set; } + + [Required] + public string LatestSupportedJavaVersion { get; set; } + [Output] public string CommandLineToolsPath { get; set; } @@ -81,6 +87,11 @@ public override bool RunTask () MonoAndroidLibPath = MonoAndroidHelper.GetOSLibPath () + Path.DirectorySeparatorChar; AndroidBinUtilsPath = MonoAndroidBinPath + "ndk" + Path.DirectorySeparatorChar; + var minVersion = Version.Parse (MinimumSupportedJavaVersion); + var maxVersion = Version.Parse (LatestSupportedJavaVersion); + + JavaSdkPath = MonoAndroidHelper.GetJdkInfo (this.CreateTaskLogger (), JavaSdkPath, minVersion, maxVersion)?.HomePath; + MonoAndroidHelper.RefreshSupportedVersions (ReferenceAssemblyPaths); try { diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ValidateJavaVersion.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ValidateJavaVersion.cs index 96ff11c975c..7d42a5c328d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ValidateJavaVersion.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ValidateJavaVersion.cs @@ -48,12 +48,12 @@ public override bool RunTask () // `java -version` will produce values such as: // java version "9.0.4" // java version "1.8.0_77" - static readonly Regex JavaVersionRegex = new Regex (@"version ""(?[\d\.]+)(_d+)?[^""]*"""); + static readonly Regex JavaVersionRegex = new Regex (@"version ""(?[\d\.]+)(_\d+)?[^""]*"""); // `javac -version` will produce values such as: // javac 9.0.4 // javac 1.8.0_77 - static readonly Regex JavacVersionRegex = new Regex (@"(?[\d\.]+)(_d+)?"); + internal static readonly Regex JavacVersionRegex = new Regex (@"(?[\d\.]+)(_\d+)?"); bool ValidateJava () { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/ResolveSdksTaskTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/ResolveSdksTaskTests.cs index 1e3a2d05267..394d6d3f1b1 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/ResolveSdksTaskTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/ResolveSdksTaskTests.cs @@ -25,6 +25,10 @@ public class ResolveSdksTaskTests : BaseTest { new ApiInfo () { Id = "Z", Level = 127, Name = "Z", FrameworkVersion = "v108.1.99", Stable = false }, }; + // via Xamarin.Android.Common.props + const string MinimumSupportedJavaVersion = "1.6.0"; + const string LatestSupportedJavaVersion = "11.0.99"; + static object [] UseLatestAndroidSdkTestCases = new object [] { new object[] { /* buildtools */ "26.0.3", @@ -167,6 +171,8 @@ public void UseLatestAndroidSdk (string buildtools, string jdk, ApiInfo[] apis, AndroidSdkPath = androidSdkPath, AndroidNdkPath = androidNdkPath, JavaSdkPath = javaPath, + MinimumSupportedJavaVersion = MinimumSupportedJavaVersion, + LatestSupportedJavaVersion = LatestSupportedJavaVersion, ReferenceAssemblyPaths = new [] { Path.Combine (referencePath, "MonoAndroid"), }, @@ -220,6 +226,8 @@ public void ResolveSdkTiming () AndroidSdkPath = androidSdkPath, AndroidNdkPath = androidNdkPath, JavaSdkPath = javaPath, + MinimumSupportedJavaVersion = MinimumSupportedJavaVersion, + LatestSupportedJavaVersion = LatestSupportedJavaVersion, ReferenceAssemblyPaths = new [] { Path.Combine (referencePath, "MonoAndroid"), }, @@ -391,6 +399,8 @@ public void TargetFrameworkPairing (string description, ApiInfo[] androidSdk, Ap AndroidSdkPath = androidSdkPath, AndroidNdkPath = androidNdkPath, JavaSdkPath = javaPath, + MinimumSupportedJavaVersion = MinimumSupportedJavaVersion, + LatestSupportedJavaVersion = LatestSupportedJavaVersion, ReferenceAssemblyPaths = new [] { Path.Combine (referencePath, "MonoAndroid"), }, diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs index 06a56115d4a..d43df6e4b3b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BaseTest.cs @@ -389,48 +389,93 @@ protected string CreateFauxReferencesDirectory (string path, ApiInfo [] versions protected string CreateFauxJavaSdkDirectory (string path, string javaVersion, out string javaExe, out string javacExe) { - javaExe = IsWindows ? "Java.cmd" : "java.bash"; - javacExe = IsWindows ? "Javac.cmd" : "javac.bash"; - var jarSigner = IsWindows ? "jarsigner.exe" : "jarsigner"; + javaExe = IsWindows ? "java.cmd" : "java"; + javacExe = IsWindows ? "javac.cmd" : "javac"; + var javaPath = Path.Combine (Root, path); - var javaBinPath = Path.Combine (javaPath, "bin"); - Directory.CreateDirectory (javaBinPath); - CreateFauxJavaExe (Path.Combine (javaBinPath, javaExe), javaVersion); - CreateFauxJavacExe (Path.Combine (javaBinPath, javacExe), javaVersion); + CreateFauxJdk (javaPath, javaVersion, javaVersion, javaVersion); + var jarSigner = IsWindows ? "jarsigner.exe" : "jarsigner"; + var javaBinPath = Path.Combine (javaPath, "bin"); File.WriteAllText (Path.Combine (javaBinPath, jarSigner), ""); + return javaPath; } - void CreateFauxJavaExe (string javaExeFullPath, string version) + // https://github.com/xamarin/xamarin-android-tools/blob/683f37508b56c76c24b3287a5687743438625341/tests/Xamarin.Android.Tools.AndroidSdk-Tests/JdkInfoTests.cs#L60-L100 + void CreateFauxJdk (string dir, string releaseVersion, string releaseBuildNumber, string javaVersion) { - var sb = new StringBuilder (); - if (IsWindows) { - sb.AppendLine ("@echo off"); - sb.AppendLine ($"echo java version \"{version}\""); - sb.AppendLine ($"echo Java(TM) SE Runtime Environment (build {version}-b13)"); - sb.AppendLine ($"echo Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)"); - } else { - sb.AppendLine ("#!/bin/bash"); - sb.AppendLine ($"echo \"java version \\\"{version}\\\"\""); - sb.AppendLine ($"echo \"Java(TM) SE Runtime Environment (build {version}-b13)\""); - sb.AppendLine ($"echo \"Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)\""); - } - CreateFauxExecutable (javaExeFullPath, sb); - } - - void CreateFauxJavacExe (string javacExeFullPath, string version) + Directory.CreateDirectory (dir); + + using (var release = new StreamWriter (Path.Combine (dir, "release"))) { + release.WriteLine ($"JAVA_VERSION=\"{releaseVersion}\""); + release.WriteLine ($"BUILD_NUMBER={releaseBuildNumber}"); + release.WriteLine ($"JUST_A_KEY"); + } + + var bin = Path.Combine (dir, "bin"); + var inc = Path.Combine (dir, "include"); + var jre = Path.Combine (dir, "jre"); + var jli = Path.Combine (jre, "lib", "jli"); + + Directory.CreateDirectory (bin); + Directory.CreateDirectory (inc); + Directory.CreateDirectory (jli); + Directory.CreateDirectory (jre); + + string quote = IsWindows ? "" : "\""; + string java = IsWindows + ? $"echo java version \"{javaVersion}\"{Environment.NewLine}" + : $"echo java version '\"{javaVersion}\"'{Environment.NewLine}"; + java = java + + $"echo Property settings:{Environment.NewLine}" + + $"echo {quote} java.home = {dir}{quote}{Environment.NewLine}" + + $"echo {quote} java.vendor = Xamarin.Android Unit Tests{quote}{Environment.NewLine}" + + $"echo {quote} java.version = {javaVersion}{quote}{Environment.NewLine}" + + $"echo {quote} xamarin.multi-line = line the first{quote}{Environment.NewLine}" + + $"echo {quote} line the second{quote}{Environment.NewLine}" + + $"echo {quote} .{quote}{Environment.NewLine}"; + + string javac = + $"echo javac {javaVersion}{Environment.NewLine}"; + + CreateShellScript (Path.Combine (bin, "jar"), ""); + CreateShellScript (Path.Combine (bin, "java"), java); + CreateShellScript (Path.Combine (bin, "javac"), javac); + CreateShellScript (Path.Combine (jli, "libjli.dylib"), ""); + CreateShellScript (Path.Combine (jre, "libjvm.so"), ""); + CreateShellScript (Path.Combine (jre, "jvm.dll"), ""); + } + + // https://github.com/xamarin/xamarin-android-tools/blob/683f37508b56c76c24b3287a5687743438625341/tests/Xamarin.Android.Tools.AndroidSdk-Tests/JdkInfoTests.cs#L108-L132 + void CreateShellScript (string path, string contents) { - var sb = new StringBuilder (); - if (IsWindows) { - sb.AppendLine ("@echo off"); - sb.AppendLine ($"echo javac {version}"); - } else { - sb.AppendLine ("#!/bin/bash"); - sb.AppendLine ($"echo \"javac {version}\""); + if (IsWindows && string.Compare (Path.GetExtension (path), ".dll", true) != 0) + path += ".cmd"; + using (var script = new StreamWriter (path)) { + if (IsWindows) { + script.WriteLine ("@echo off"); + } + else { + script.WriteLine ("#!/bin/sh"); + } + script.WriteLine (contents); } - CreateFauxExecutable (javacExeFullPath, sb); + if (IsWindows) + return; + var chmod = new ProcessStartInfo { + FileName = "chmod", + Arguments = $"+x \"{path}\"", + UseShellExecute = false, + RedirectStandardInput = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + }; + var p = Process.Start (chmod); + p.WaitForExit (); } void CreateFauxExecutable (string exeFullPath, StringBuilder sb) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 2009451e793..1490bcb8ba8 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -122,6 +122,19 @@ public static void RefreshSupportedVersions (string[] referenceAssemblyPaths) } #endif // MSBUILD + public static JdkInfo GetJdkInfo (Action logger, string javaSdkPath, Version minSupportedVersion, Version maxSupportedVersion) + { + JdkInfo info = null; + try { + info = new JdkInfo (javaSdkPath); + } catch { + info = JdkInfo.GetKnownSystemJdkInfos (logger) + .Where (jdk => jdk.Version >= minSupportedVersion && jdk.Version <= maxSupportedVersion) + .FirstOrDefault (); + } + return info; + } + class SizeAndContentFileComparer : IEqualityComparer #if MSBUILD , IEqualityComparer diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets index 89f8f520550..bff4c81f9cb 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Tooling.targets @@ -67,6 +67,8 @@ projects. AndroidSdkPath="$(AndroidSdkDirectory)" AndroidNdkPath="$(AndroidNdkDirectory)" JavaSdkPath="$(JavaSdkDirectory)" + MinimumSupportedJavaVersion="$(MinimumSupportedJavaVersion)" + LatestSupportedJavaVersion="$(LatestSupportedJavaVersion)" ReferenceAssemblyPaths="$(_XATargetFrameworkDirectories)"> @@ -82,6 +84,8 @@ projects.