From af98a44420f7fd6b1888558a05ef5a51c9977d96 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 1 Jul 2019 11:23:53 -0500 Subject: [PATCH 1/3] [Windows] check %JAVA_HOME% for locating Jdk Fixes: http://feedback.devdiv.io/606218 Something has happened with Xamarin.Android builds running on Azure DevOps recently, builds are now failing with: ResolveSdks ... Errors C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Xamarin\Android\Xamarin.Android.Common.targets(721,2): error XA5300: The Java SDK Directory could not be found. Please set via /p:JavaSdkDirectory. [d:\a\1\s\Xamarin.Android.Lite\Xamarin.Android.Lite.csproj] Azure DevOps has posted a "workaround" document: https://github.com/microsoft/azure-pipelines-image-generation/blob/1371aa8548aa135c3be783bd854681118ca10f78/images/win/Vs2019-Server2019-Readme.md#known-issues It suggests adding this property to MSBuild calls: /p:JavaSdkDirectory="$(JAVA_HOME_8_X64)" Reviewing a user's build log from ReactiveUI: https://dev.azure.com/dotnet/ReactiveUI/_build/results?buildId=18505 /p:JavaSdkDirectory=C:/Program%20Files/Java/zulu-8-azure-jdk_8.38.0.13-8.0.212-win_x64 So I tried this with my own repo that has Azure DevOps configured, sure enough a build from June 18, 2019 worked. A rebuild on July 1, 2019 failed with the reported error. * Worked: https://jopepper.visualstudio.com/Jon%20Peppers%20OSS/_build/results?buildId=208 * Failed: https://jopepper.visualstudio.com/Jon%20Peppers%20OSS/_build/results?buildId=209 I can read from the build logs that both `JAVA_HOME` and `JAVA_HOME_8_X64` are set to: C:/Program Files/Java/zulu-8-azure-jdk_8.38.0.13-8.0.212-win_x64 I think this means two things: * Microsoft Open JDK is no longer installed on the `Hosted Windows 2019 with VS2019` pool. * Instead, *yet another JDK* called the Azul Zulu JDK is present... https://docs.microsoft.com/en-us/java/azure/jdk/java-jdk-install?view=azure-java-stable I think we should add a "last resort" lookup for a valid JDK within `JAVA_HOME` on Windows. It does not currently do that. Reviewing the code, I am somewhat confused by `JdkInfo`. Its logic is not run on Windows, and instead we have Windows-specific JDK lookups in `AndroidSdkWindows`. To make things work properly we can just add a last chance lookup for `JAVA_HOME` in `AndroidSdkWindows`. I also cleaned up the tests a bit: `CreateFauxJavaSdkDirectory` was not setting up `jar.exe` or `jvm.dll`. --- .../Sdks/AndroidSdkWindows.cs | 14 +++++++- .../Tests/AndroidSdkInfoTests.cs | 33 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs index 549267e..9ee716f 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs @@ -135,7 +135,19 @@ IEnumerable ToJdkInfos (IEnumerable paths, string locator) return ToJdkInfos (GetPreferredJdkPaths (), "Preferred Registry") .Concat (ToJdkInfos (GetOpenJdkPaths (), "OpenJDK")) .Concat (ToJdkInfos (GetKnownOpenJdkPaths (), "Well-known OpenJDK paths")) - .Concat (ToJdkInfos (GetOracleJdkPaths (), "Oracle JDK")); + .Concat (ToJdkInfos (GetOracleJdkPaths (), "Oracle JDK")) + .Concat (ToJdkInfos (GetEnvironmentJdkPaths (), "Environment Variables")); + } + + private static IEnumerable GetEnvironmentJdkPaths () + { + var environment = new [] { "JAVA_HOME" }; + foreach (var key in environment) { + var value = Environment.GetEnvironmentVariable (key); + if (!string.IsNullOrEmpty (value)) { + yield return value; + } + } } private static IEnumerable GetPreferredJdkPaths () diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs b/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs index ff69f54..e19a3c3 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs @@ -135,6 +135,33 @@ public void Constructor_SetValuesFromPath () } } + [Test] + public void JdkDirectory_JavaHome () + { + CreateSdks (out string root, out string jdk, out string ndk, out string sdk); + + var logs = new StringWriter (); + Action logger = (level, message) => { + logs.WriteLine ($"[{level}] {message}"); + }; + + string java_home = null; + try { + // We only set via JAVA_HOME + java_home = Environment.GetEnvironmentVariable ("JAVA_HOME", EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable ("JAVA_HOME", jdk); + var info = new AndroidSdkInfo (logger, androidSdkPath: sdk, androidNdkPath: ndk, javaSdkPath: ""); + + Assert.AreEqual (ndk, info.AndroidNdkPath, "AndroidNdkPath not preserved!"); + Assert.AreEqual (sdk, info.AndroidSdkPath, "AndroidSdkPath not preserved!"); + Assert.AreEqual (jdk, info.JavaSdkPath, "JavaSdkPath not preserved!"); + } finally { + if (java_home != null) + Environment.SetEnvironmentVariable ("JAVA_HOME", java_home, EnvironmentVariableTarget.Process); + Directory.Delete (root, recursive: true); + } + } + static bool IsWindows => OS.IsWindows; static void CreateSdks (out string root, out string jdk, out string ndk, out string sdk) @@ -218,15 +245,21 @@ static string CreateFauxJavaSdkDirectory (string javaPath, string javaVersion, o { javaExe = IsWindows ? "Java.cmd" : "java.bash"; javacExe = IsWindows ? "Javac.cmd" : "javac.bash"; + var jar = IsWindows ? "Jar.cmd" : "jar"; var jarSigner = IsWindows ? "jarsigner.exe" : "jarsigner"; + var jvm = IsWindows ? "Jvm.dll" : "libjvm.dylib"; var javaBinPath = Path.Combine (javaPath, "bin"); + var jrePath = Path.Combine (javaPath, "jre"); Directory.CreateDirectory (javaBinPath); + Directory.CreateDirectory (jrePath); CreateFauxJavaExe (Path.Combine (javaBinPath, javaExe), javaVersion); CreateFauxJavacExe (Path.Combine (javaBinPath, javacExe), javaVersion); + File.WriteAllText (Path.Combine (javaBinPath, jar), ""); File.WriteAllText (Path.Combine (javaBinPath, jarSigner), ""); + File.WriteAllText (Path.Combine (jrePath, jvm), ""); return javaPath; } From 06f7b50aeb70898532bbc1bfe5a9c1aadad2e98a Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 1 Jul 2019 11:59:09 -0500 Subject: [PATCH 2/3] Use JdkInfoTests.CreateFauxJdk --- .../Tests/AndroidSdkInfoTests.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs b/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs index e19a3c3..c475b72 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs @@ -139,6 +139,7 @@ public void Constructor_SetValuesFromPath () public void JdkDirectory_JavaHome () { CreateSdks (out string root, out string jdk, out string ndk, out string sdk); + JdkInfoTests.CreateFauxJdk (jdk, releaseVersion: "1.8.999", releaseBuildNumber: "9", javaVersion: "1.8.999-9"); var logs = new StringWriter (); Action logger = (level, message) => { @@ -245,21 +246,15 @@ static string CreateFauxJavaSdkDirectory (string javaPath, string javaVersion, o { javaExe = IsWindows ? "Java.cmd" : "java.bash"; javacExe = IsWindows ? "Javac.cmd" : "javac.bash"; - var jar = IsWindows ? "Jar.cmd" : "jar"; var jarSigner = IsWindows ? "jarsigner.exe" : "jarsigner"; - var jvm = IsWindows ? "Jvm.dll" : "libjvm.dylib"; var javaBinPath = Path.Combine (javaPath, "bin"); - var jrePath = Path.Combine (javaPath, "jre"); Directory.CreateDirectory (javaBinPath); - Directory.CreateDirectory (jrePath); CreateFauxJavaExe (Path.Combine (javaBinPath, javaExe), javaVersion); CreateFauxJavacExe (Path.Combine (javaBinPath, javacExe), javaVersion); - File.WriteAllText (Path.Combine (javaBinPath, jar), ""); File.WriteAllText (Path.Combine (javaBinPath, jarSigner), ""); - File.WriteAllText (Path.Combine (jrePath, jvm), ""); return javaPath; } From 8ce17b0ec8182482713353b36d53ae0607d16226 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 1 Jul 2019 12:11:44 -0500 Subject: [PATCH 3/3] Ignore test I can't actually run this test on CI, since our current CI machines have the Microsoft Open JDK installed. --- .../Tests/AndroidSdkInfoTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs b/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs index c475b72..50c6d9a 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs @@ -136,6 +136,7 @@ public void Constructor_SetValuesFromPath () } [Test] + [Ignore ("This test will only work locally if you rename/remove your Open JDK directory.")] public void JdkDirectory_JavaHome () { CreateSdks (out string root, out string jdk, out string ndk, out string sdk);