diff --git a/Xamarin.Android.Tools.sln b/Xamarin.Android.Tools.sln index 9dd35bc..13ebd9a 100644 --- a/Xamarin.Android.Tools.sln +++ b/Xamarin.Android.Tools.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30709.64 MinimumVisualStudioVersion = 10.0.40219.1 @@ -10,6 +10,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Android.Build.Bas EndProject 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}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{19FE1B44-DB71-4F97-A07E-085888690DAE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ls-jdks", "tools\ls-jdks\ls-jdks.csproj", "{3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +36,10 @@ Global {4F5FD01C-B5A6-4F9F-91CE-3E78B3F942AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F5FD01C-B5A6-4F9F-91CE-3E78B3F942AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F5FD01C-B5A6-4F9F-91CE-3E78B3F942AC}.Release|Any CPU.Build.0 = Release|Any CPU + {3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -39,4 +47,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BA1CD771-F00B-4DE8-93EE-7690D81F6A5A} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3D2ADF77-62C7-4E0E-AB29-BDD266B7D56D} = {19FE1B44-DB71-4F97-A07E-085888690DAE} + EndGlobalSection EndGlobal diff --git a/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs b/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs index 7f6cd0c..feb5865 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs @@ -185,7 +185,7 @@ public static void DetectAndSetPreferredJavaSdkPathToLatest (Action GetKnownSystemJdkInfos (Action GetKnownSystemJdkInfos (Action GetConfiguredJdks (Action logger) - { - return GetConfiguredJdkPaths (logger) - .Select (p => TryGetJdkInfo (p, logger, "monodroid-config.xml")) - .Where (jdk => jdk != null) - .Select (jdk => jdk!) - .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); - } - - static IEnumerable GetConfiguredJdkPaths (Action logger) - { - var config = AndroidSdkUnix.GetUnixConfigFile (logger); - foreach (var java_sdk in config.Root.Elements ("java-sdk")) { - var path = (string) java_sdk.Attribute ("path"); - yield return path; - } - } - - internal static IEnumerable GetMicrosoftOpenJdks (Action logger) - { - foreach (var dir in GetMacOSMicrosoftOpenJdks (logger)) - yield return dir; - if (Path.DirectorySeparatorChar != '\\') - yield break; - foreach (var dir in AndroidSdkWindows.GetJdkInfos (logger)) { - yield return dir; - } - } - - static IEnumerable GetMacOSMicrosoftOpenJdks (Action logger) - { - return GetMacOSMicrosoftOpenJdkPaths () - .Select (p => TryGetJdkInfo (p, logger, "/Library/Java/JavaVirtualMachines/microsoft-*.jdk")) - .Where (jdk => jdk != null) - .Select (jdk => jdk!) - .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); - } - - static IEnumerable GetMacOSMicrosoftOpenJdkPaths () - { - var root = "/Library/Java/JavaVirtualMachines"; - var pattern = "microsoft-*.jdk"; - var toHome = Path.Combine ("Contents", "Home"); - var jdks = AppDomain.CurrentDomain.GetData ($"GetMacOSMicrosoftJdkPaths jdks override! {typeof (JdkInfo).AssemblyQualifiedName}") - ?.ToString (); - if (jdks != null) { - root = jdks; - toHome = ""; - pattern = "*"; - } - if (!Directory.Exists (root)) { - yield break; - } - foreach (var dir in Directory.EnumerateDirectories (root, pattern)) { - var home = Path.Combine (dir, toHome); - if (!Directory.Exists (home)) - continue; - yield return home; - } - } - - static JdkInfo? TryGetJdkInfo (string path, Action logger, string locator) + internal static JdkInfo? TryGetJdkInfo (string path, Action logger, string locator) { JdkInfo? jdk = null; try { @@ -369,13 +311,6 @@ static IEnumerable GetMacOSMicrosoftOpenJdkPaths () return jdk; } - static IEnumerable GetWindowsJdks (Action logger) - { - if (!OS.IsWindows) - return Enumerable.Empty (); - return AndroidSdkWindows.GetJdkInfos (logger); - } - static IEnumerable GetEnvironmentVariableJdks (string envVar, Action logger) { var java_home = Environment.GetEnvironmentVariable (envVar); diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/AzulJdkLocations.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/AzulJdkLocations.cs new file mode 100644 index 0000000..13d10c1 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/AzulJdkLocations.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace Xamarin.Android.Tools { + + class AzulJdkLocations : JdkLocations { + + internal static IEnumerable GetAzulJdks (Action logger) + { + return GetMacOSSystemJdks ("zulu-*.jdk", logger) + .Concat (GetWindowsFileSystemJdks (Path.Combine ("Zulu", "zulu-*"), logger)) + .Concat (GetWindowsRegistryJdks (logger, @"SOFTWARE\Azul Systems\Zulu", "zulu-*", null, "InstallationPath")) + .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.MacOS.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.MacOS.cs new file mode 100644 index 0000000..dc2a8f5 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.MacOS.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Tools { + + partial class JdkLocations { + + static IEnumerable GetUnixPreferredJdks (Action logger) + { + return GetUnixConfiguredJdkPaths (logger) + .Select (p => JdkInfo.TryGetJdkInfo (p, logger, "monodroid-config.xml")) + .Where (jdk => jdk != null) + .Select (jdk => jdk!) + .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); + } + + static IEnumerable GetUnixConfiguredJdkPaths (Action logger) + { + var config = AndroidSdkUnix.GetUnixConfigFile (logger); + foreach (var java_sdk in config.Root.Elements ("java-sdk")) { + var path = (string) java_sdk.Attribute ("path"); + yield return path; + } + } + + const string MacOSJavaVirtualMachinesRoot = "/Library/Java/JavaVirtualMachines"; + + protected static IEnumerable GetMacOSSystemJdks (string pattern, Action logger, string? locator = null) + { + if (!OS.IsMac) { + return Array.Empty(); + } + locator = locator ?? Path.Combine (MacOSJavaVirtualMachinesRoot, pattern); + return FromPaths (GetMacOSSystemJvmPaths (pattern), logger, locator); + } + + static IEnumerable GetMacOSSystemJvmPaths (string pattern) + { + var root = MacOSJavaVirtualMachinesRoot; + var toHome = Path.Combine ("Contents", "Home"); + var jdks = AppDomain.CurrentDomain.GetData ($"GetMacOSMicrosoftJdkPaths jdks override! {typeof (JdkInfo).AssemblyQualifiedName}") + ?.ToString (); + if (jdks != null) { + root = jdks; + toHome = ""; + pattern = "*"; + } + if (!Directory.Exists (root)) { + yield break; + } + foreach (var dir in Directory.EnumerateDirectories (root, pattern)) { + var home = Path.Combine (dir, toHome); + if (!Directory.Exists (home)) + continue; + yield return home; + } + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.Windows.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.Windows.cs new file mode 100644 index 0000000..2380ce6 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.Windows.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Xamarin.Android.Tools { + + partial class JdkLocations { + + internal static string? GetWindowsPreferredJdkPath () + { + var wow = RegistryEx.Wow64.Key32; + var regKey = AndroidSdkWindows.GetMDRegistryKey (); + if (RegistryEx.CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, regKey, AndroidSdkWindows.MDREG_JAVA_SDK, wow, "bin", "java.exe")) + return RegistryEx.GetValueString (RegistryEx.CurrentUser, regKey, AndroidSdkWindows.MDREG_JAVA_SDK, wow); + return null; + + } + + protected static IEnumerable GetWindowsFileSystemJdks (string pattern, Action logger, string? locator = null) + { + if (!OS.IsWindows) { + yield break; + } + + var roots = new List() { + // "Compatibility" with existing codebases; should probably be avoided… + Environment.ExpandEnvironmentVariables ("%ProgramW6432%"), + Environment.GetFolderPath (Environment.SpecialFolder.ProgramFiles), + Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), + }; + foreach (var root in roots) { + if (!Directory.Exists (root)) + continue; + IEnumerable homes; + try { + homes = Directory.EnumerateDirectories (root, pattern); + } + catch (IOException e) { + continue; + } + foreach (var home in homes) { + if (!File.Exists (Path.Combine (home, "bin", "java.exe"))) + continue; + var loc = locator ?? $"{pattern} via {root}"; + var jdk = JdkInfo.TryGetJdkInfo (home, logger, loc); + if (jdk == null) + continue; + yield return jdk; + } + } + } + + protected static IEnumerable GetWindowsRegistryJdks ( + Action logger, + string parentKey, + string childKeyGlob, + string? grandChildKey, + string valueName, + string? locator = null) + { + if (!OS.IsWindows) { + yield break; + } + + var regex = ToRegex (childKeyGlob); + var paths = new List<(Version version, string path)> (); + var roots = new[] { RegistryEx.CurrentUser, RegistryEx.LocalMachine }; + var wows = new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 }; + foreach (var root in roots) + foreach (var wow in wows) { + foreach (var subkeyName in RegistryEx.EnumerateSubkeys (root, parentKey, wow)) { + if (!regex.Match (subkeyName).Success) { + continue; + } + var key = $@"{parentKey}\{subkeyName}" + + (grandChildKey == null ? "" : "\\" + grandChildKey); + var path = RegistryEx.GetValueString (root, key, valueName, wow); + if (path == null) { + continue; + } + var jdk = JdkInfo.TryGetJdkInfo (path, logger, locator ?? $"Windows Registry @ {parentKey}"); + if (jdk == null) { + continue; + } + yield return jdk; + } + } + } + + static Regex ToRegex (string glob) + { + var r = new StringBuilder (); + foreach (char c in glob) { + switch (c) { + case '*': + r.Append (".*"); + break; + case '?': + r.Append ("."); + break; + default: + r.Append (Regex.Escape (c.ToString ())); + break; + } + } + return new Regex (r.ToString (), RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.cs new file mode 100644 index 0000000..9f87523 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/JdkLocations.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace Xamarin.Android.Tools { + + partial class JdkLocations { + + internal static IEnumerable GetPreferredJdks (Action logger) + { + if (OS.IsWindows) { + var path = GetWindowsPreferredJdkPath (); + var jdk = path == null ? null : JdkInfo.TryGetJdkInfo (path, logger, "Windows Registry"); + if (jdk != null) + yield return jdk; + } + foreach (var jdk in GetUnixPreferredJdks (logger)) { + yield return jdk; + } + } + + protected static IEnumerable FromPaths (IEnumerable paths, Action logger, string locator) + { + return paths + .Select (p => JdkInfo.TryGetJdkInfo (p, logger, locator)) + .Where (jdk => jdk != null) + .Select (jdk => jdk!); + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/MicrosoftOpenJdkLocations.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/MicrosoftOpenJdkLocations.cs new file mode 100644 index 0000000..9274eb6 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/MicrosoftOpenJdkLocations.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace Xamarin.Android.Tools { + + class MicrosoftOpenJdkLocations : JdkLocations { + + internal static IEnumerable GetMicrosoftOpenJdks (Action logger) + { + return GetMacOSSystemJdks ("microsoft-*.jdk", logger) + .Concat (GetWindowsFileSystemJdks (Path.Combine ("Microsoft", "jdk-*"), logger)) + .Concat (GetWindowsRegistryJdks (logger, @"SOFTWARE\Microsoft\JDK", "*", @"hotspot\MSI", "Path")) + .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/OracleJdkLocations.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/OracleJdkLocations.cs new file mode 100644 index 0000000..0e2a3b0 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/OracleJdkLocations.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace Xamarin.Android.Tools { + + class OracleJdkLocations : JdkLocations { + + internal static IEnumerable GetOracleJdks (Action logger) + { + if (!OS.IsWindows) { + yield break; + } + foreach (var path in GetOracleJdkPaths ()) { + var jdk = JdkInfo.TryGetJdkInfo (path, logger, "Oracle Registry"); + if (jdk == null) { + continue; + } + yield return jdk; + } + } + + static IEnumerable GetOracleJdkPaths () + { + string subkey = @"SOFTWARE\JavaSoft\Java Development Kit"; + + foreach (var wow64 in new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 }) { + string key_name = string.Format (@"{0}\{1}\{2}", "HKLM", subkey, "CurrentVersion"); + var currentVersion = RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey, "CurrentVersion", wow64); + + if (!string.IsNullOrEmpty (currentVersion)) { + + if (RegistryEx.CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64, "bin", "java.exe")) + yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64) ?? ""; + } + } + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/VSAndroidJdkLocations.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/VSAndroidJdkLocations.cs new file mode 100644 index 0000000..2c5dcc9 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/VSAndroidJdkLocations.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace Xamarin.Android.Tools { + + class VSAndroidJdkLocations : JdkLocations { + + internal static IEnumerable GetVSAndroidJdks (Action logger) + { + if (!OS.IsWindows) { + yield break; + } + var root = RegistryEx.LocalMachine; + var wows = new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 }; + var subKey = @"SOFTWARE\Microsoft\VisualStudio\Android"; + var valueName = "JavaHome"; + + foreach (var wow in wows) { + if (!RegistryEx.CheckRegistryKeyForExecutable (root, subKey, valueName, wow, "bin", "java.exe")) { + continue; + } + var path = RegistryEx.GetValueString (root, subKey, valueName, wow) ?? ""; + if (string.IsNullOrEmpty (path)) { + continue; + } + var jdk = JdkInfo.TryGetJdkInfo (path, logger, subKey); + if (jdk == null) { + continue; + } + yield return jdk; + } + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Jdks/XAPrepareJdkLocations.cs b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/XAPrepareJdkLocations.cs new file mode 100644 index 0000000..80c1ed8 --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/Jdks/XAPrepareJdkLocations.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace Xamarin.Android.Tools { + + class XAPrepareJdkLocations : JdkLocations { + + internal static IEnumerable GetXAPrepareJdks (Action logger) + { + return FromPaths (GetXAPrepareJdkPaths (), logger, "android-toolchain"); + } + + static IEnumerable GetXAPrepareJdkPaths () + { + var androidToolchainDir = Path.Combine (Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "android-toolchain"); + if (!Directory.Exists (androidToolchainDir)) { + return Array.Empty (); + } + return Directory.EnumerateDirectories (androidToolchainDir, "jdk*"); + } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/OS.cs b/src/Xamarin.Android.Tools.AndroidSdk/OS.cs index 338b0fd..e0e07f7 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/OS.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/OS.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.IO; +using System.Linq; using System.Text; namespace Xamarin.Android.Tools @@ -173,6 +174,27 @@ IntPtr lpftLastWriteTime [DllImport ("advapi32.dll", SetLastError = true)] static extern int RegCloseKey (UIntPtr hKey); + internal static bool CheckRegistryKeyForExecutable (UIntPtr key, string subkey, string valueName, RegistryEx.Wow64 wow64, string subdir, string exe) + { + try { + string key_name = string.Format (@"{0}\{1}\{2}", key == RegistryEx.CurrentUser ? "HKCU" : "HKLM", subkey, valueName); + + var path = AndroidSdkBase.NullIfEmpty (RegistryEx.GetValueString (key, subkey, valueName, wow64)); + + if (path == null) { + return false; + } + + if (!ProcessUtils.FindExecutablesInDirectory (Path.Combine (path, subdir), exe).Any ()) { + return false; + } + + return true; + } catch (Exception) { + return false; + } + } + public static IEnumerable EnumerateSubkeys (UIntPtr key, string subkey, Wow64 wow64) { UIntPtr regKeyHandle; diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs index 6aa64b8..e26233b 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs @@ -279,7 +279,7 @@ public bool ValidateAndroidNdkLocation ([NotNullWhen (true)] string? loc) return result; } - protected static string? NullIfEmpty (string? s) + internal static string? NullIfEmpty (string? s) { if (s == null || s.Length != 0) return s; diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs index 81c909c..c330984 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs @@ -8,12 +8,14 @@ namespace Xamarin.Android.Tools { + using static RegistryEx; + class AndroidSdkWindows : AndroidSdkBase { const string MDREG_KEY = @"SOFTWARE\Novell\Mono for Android"; const string MDREG_ANDROID_SDK = "AndroidSdkDirectory"; const string MDREG_ANDROID_NDK = "AndroidNdkDirectory"; - const string MDREG_JAVA_SDK = "JavaSdkDirectory"; + internal const string MDREG_JAVA_SDK = "JavaSdkDirectory"; const string ANDROID_INSTALLER_PATH = @"SOFTWARE\Android SDK Tools"; const string ANDROID_INSTALLER_KEY = "Path"; const string XAMARIN_ANDROID_INSTALLER_PATH = @"SOFTWARE\Xamarin\MonoAndroid"; @@ -63,7 +65,7 @@ public override string? PreferedJavaSdkPath { } } - static string GetMDRegistryKey () + internal static string GetMDRegistryKey () { var regKey = Environment.GetEnvironmentVariable ("XAMARIN_ANDROID_REGKEY"); return string.IsNullOrWhiteSpace (regKey) ? MDREG_KEY : regKey; @@ -108,165 +110,6 @@ protected override IEnumerable GetAllAvailableAndroidSdks () yield return basePath; } - internal static IEnumerable GetJdkInfos (Action logger) - { - JdkInfo? TryGetJdkInfo (string path, string locator) - { - JdkInfo? jdk = null; - try { - jdk = new JdkInfo (path, locator); - } - catch (Exception e) { - logger (TraceLevel.Warning, string.Format (Resources.InvalidJdkDirectory_path_locator_message, path, locator, e.Message)); - logger (TraceLevel.Verbose, e.ToString ()); - } - return jdk; - } - - IEnumerable ToJdkInfos (IEnumerable paths, string locator) - { - return paths.Select (p => TryGetJdkInfo (p, locator)) - .Where (jdk => jdk != null) - .Select(jdk => jdk!) - .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); - } - - return ToJdkInfos (GetPreferredJdkPaths (), "Preferred Registry") - .Concat (ToJdkInfos (GetMicrosoftOpenJdkFilesystemPaths (), "Microsoft OpenJDK Filesystem")) - .Concat (ToJdkInfos (GetMicrosoftOpenJdkRegistryPaths (), "Microsoft OpenJDK Registry")) - .Concat (ToJdkInfos (GetVSAndroidJdkPaths (), @"HKLM\SOFTWARE\Microsoft\VisualStudio\Android@JavaHome")) - .Concat (ToJdkInfos (GetOracleJdkPaths (), "Oracle JDK")) - ; - } - - private static IEnumerable GetPreferredJdkPaths () - { - // check the user specified path - var roots = new[] { RegistryEx.CurrentUser, RegistryEx.LocalMachine }; - const RegistryEx.Wow64 wow = RegistryEx.Wow64.Key32; - var regKey = GetMDRegistryKey (); - - foreach (var root in roots) { - if (CheckRegistryKeyForExecutable (root, regKey, MDREG_JAVA_SDK, wow, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (root, regKey, MDREG_JAVA_SDK, wow) ?? ""; - } - } - - private static IEnumerable GetVSAndroidJdkPaths () - { - var root = RegistryEx.LocalMachine; - var wows = new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 }; - var subKey = @"SOFTWARE\Microsoft\VisualStudio\Android"; - var valueName = "JavaHome"; - - foreach (var wow in wows) { - if (CheckRegistryKeyForExecutable (root, subKey, valueName, wow, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (root, subKey, valueName, wow) ?? ""; - } - } - - static IEnumerable GetMicrosoftOpenJdkFilesystemPaths () - { - const string JdkFolderNamePrefix = "jdk-"; - - var paths = new List> (); - var rootPaths = new List { - Path.Combine (Environment.ExpandEnvironmentVariables ("%ProgramW6432%"), "Microsoft"), - Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "Android", "jdk"), - }; - - foreach (var rootPath in rootPaths) { - if (!Directory.Exists (rootPath)) - continue; - foreach (var directoryName in Directory.EnumerateDirectories (rootPath, $"{JdkFolderNamePrefix}*")) { - var version = ExtractVersion (directoryName, JdkFolderNamePrefix); - if (version == null) - continue; - paths.Add (Tuple.Create (directoryName, version)); - } - } - - return paths.OrderByDescending (v => v.Item2) - .Where (openJdk => ProcessUtils.FindExecutablesInDirectory (Path.Combine (openJdk.Item1, "bin"), _JarSigner).Any ()) - .Select (openJdk => openJdk.Item1); - } - - static IEnumerable GetMicrosoftOpenJdkRegistryPaths () - { - var paths = new List<(Version version, string path)> (); - var roots = new[] { RegistryEx.CurrentUser, RegistryEx.LocalMachine }; - var wows = new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 }; - foreach (var root in roots) - foreach (var wow in wows) { - foreach (var subkeyName in RegistryEx.EnumerateSubkeys (root, MICROSOFT_OPENJDK_PATH, wow)) { - if (!Version.TryParse (subkeyName, out var version)) - continue; - var msiKey = $@"{MICROSOFT_OPENJDK_PATH}\{subkeyName}\hotspot\MSI"; - var path = RegistryEx.GetValueString (root, msiKey, "Path", wow); - if (path == null) - continue; - paths.Add ((version, path)); - } - } - - return paths.OrderByDescending (e => e.version) - .Select (e => e.path); - } - - internal static Version? ExtractVersion (string path, string prefix) - { - var name = Path.GetFileName (path); - if (name.Length <= prefix.Length) - return null; - if (!name.StartsWith (prefix, StringComparison.OrdinalIgnoreCase)) - return null; - - var start = prefix.Length; - while (start < name.Length && !char.IsDigit (name, start)) { - ++start; - } - if (start == name.Length) - return null; - - name = name.Substring (start); - int end = 0; - while (end < name.Length && - (char.IsDigit (name [end]) || name [end] == '.')) { - end++; - } - - do { - if (Version.TryParse (name.Substring (0, end), out var v)) - return v; - end = name.LastIndexOf ('.', end-1); - } while (end > 0); - - return null; - } - - private static IEnumerable GetOracleJdkPaths () - { - string subkey = @"SOFTWARE\JavaSoft\Java Development Kit"; - - foreach (var wow64 in new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 }) { - string key_name = string.Format (@"{0}\{1}\{2}", "HKLM", subkey, "CurrentVersion"); - var currentVersion = RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey, "CurrentVersion", wow64); - - if (!string.IsNullOrEmpty (currentVersion)) { - - // No matter what the CurrentVersion is, look for 1.6 or 1.7 or 1.8 - if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64) ?? ""; - - if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.7", "JavaHome", wow64, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.7", "JavaHome", wow64) ?? ""; - - if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.6", "JavaHome", wow64, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.6", "JavaHome", wow64) ?? ""; - } - } - } - protected override IEnumerable GetAllAvailableAndroidNdks () { @@ -333,29 +176,6 @@ public override void SetPreferredAndroidNdkPath (string? path) RegistryEx.SetValueString (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_NDK, path ?? "", RegistryEx.Wow64.Key32); } - #region Helper Methods - private static bool CheckRegistryKeyForExecutable (UIntPtr key, string subkey, string valueName, RegistryEx.Wow64 wow64, string subdir, string exe) - { - try { - string key_name = string.Format (@"{0}\{1}\{2}", key == RegistryEx.CurrentUser ? "HKCU" : "HKLM", subkey, valueName); - - var path = NullIfEmpty (RegistryEx.GetValueString (key, subkey, valueName, wow64)); - - if (path == null) { - return false; - } - - if (!ProcessUtils.FindExecutablesInDirectory (Path.Combine (path, subdir), exe).Any ()) { - return false; - } - - return true; - } catch (Exception) { - return false; - } - } - #endregion - public override void Initialize (string? androidSdkPath = null, string? androidNdkPath = null, string? javaSdkPath = null) { base.Initialize (androidSdkPath, androidNdkPath, javaSdkPath); diff --git a/tests/Xamarin.Android.Tools.AndroidSdk-Tests/AndroidSdkWindowsTests.cs b/tests/Xamarin.Android.Tools.AndroidSdk-Tests/AndroidSdkWindowsTests.cs index cde9f65..73232a4 100644 --- a/tests/Xamarin.Android.Tools.AndroidSdk-Tests/AndroidSdkWindowsTests.cs +++ b/tests/Xamarin.Android.Tools.AndroidSdk-Tests/AndroidSdkWindowsTests.cs @@ -13,56 +13,5 @@ namespace Xamarin.Android.Tools.Tests [TestFixture] public class AndroidSdkWindowsTests { - [Test] - public void ExtractVersion () - { - var sep = Path.DirectorySeparatorChar; - - var tests = new[]{ - new { - Path = $"foo{sep}", - Prefix = "", - Expected = (Version) null, - }, - new { - Path = $"foo{sep}bar-1-extra", - Prefix = "bar-", - Expected = (Version) null, - }, - new { - Path = $"foo{sep}abcdef", - Prefix = "a", - Expected = (Version) null, - }, - new { - Path = $"foo{sep}a{sep}b.c.d", - Prefix = "none-of-the-above", - Expected = (Version) null, - }, - new { - Path = $"jdks{sep}jdk-1.2.3-hotspot-extra", - Prefix = "jdk-", - Expected = new Version (1, 2, 3), - }, - new { - Path = $"jdks{sep}jdk-1.2.3-hotspot-extra", - Prefix = "jdk", - Expected = new Version (1, 2, 3), - }, - new { - Path = $"jdks{sep}jdk-1.2.3.4.5.6-extra", - Prefix = "jdk-", - Expected = new Version (1, 2, 3, 4), - }, - }; - - foreach (var test in tests) { - Assert.AreEqual ( - test.Expected, - AndroidSdkWindows.ExtractVersion (test.Path, test.Prefix), - $"Version couldn't be extracted from Path=`{test.Path}` Prefix=`{test.Prefix}`!" - ); - } - } } } diff --git a/tools/ls-jdks/App.cs b/tools/ls-jdks/App.cs new file mode 100644 index 0000000..407dc4b --- /dev/null +++ b/tools/ls-jdks/App.cs @@ -0,0 +1,15 @@ +using System; + +namespace Xamarin.Android.Tools +{ + class App + { + static void Main(string[] args) + { + foreach (var jdk in JdkInfo.GetKnownSystemJdkInfos ()) { + Console.WriteLine ($"Found JDK: {jdk.HomePath}"); + Console.WriteLine ($" Locator: {jdk.Locator}"); + } + } + } +} diff --git a/tools/ls-jdks/ls-jdks.csproj b/tools/ls-jdks/ls-jdks.csproj new file mode 100644 index 0000000..751f34d --- /dev/null +++ b/tools/ls-jdks/ls-jdks.csproj @@ -0,0 +1,15 @@ + + + + Exe + net472;netcoreapp3.1 + Xamarin.Android.Tools + false + $(ToolOutputFullPath) + + + + + + +