diff --git a/.gitmodules b/.gitmodules index 60d4e416e..792f80b15 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,7 @@ path = external/cecil url = https://github.com/mono/cecil.git branch = master +[submodule "external/xamarin-android-tools"] + path = external/xamarin-android-tools + url = https://github.com/xamarin/xamarin-android-tools.git + branch = master diff --git a/Configuration.props b/Configuration.props index 4ba4b8b61..e7c9e4acb 100644 --- a/Configuration.props +++ b/Configuration.props @@ -16,8 +16,10 @@ $(MSBuildThisFileDirectory)external\cecil $(MSBuildThisFileDirectory)bin\$(Configuration)\ + $(MSBuildThisFileDirectory)external\xamarin-android-tools $([System.IO.Path]::GetFullPath ('$(CecilSourceDirectory)')) + $([System.IO.Path]::GetFullPath ('$(XamarinAndroidToolsDirectory)')) diff --git a/Java.Interop.sln b/Java.Interop.sln index 22fc435d7..f924d2ddc 100644 --- a/Java.Interop.sln +++ b/Java.Interop.sln @@ -17,7 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{4C173212-3 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop", "src\Java.Interop\Java.Interop.csproj", "{94BD81F7-B06F-4295-9636-F8A3B6BDC762}" EndProject -Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "java-interop", "src\java-interop\java-interop.csproj", "{BB0AB9F7-0979-41A7-B7A9-877260655F94}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "java-interop", "src\java-interop\java-interop.csproj", "{BB0AB9F7-0979-41A7-B7A9-877260655F94}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.Dynamic", "src\Java.Interop.Dynamic\Java.Interop.Dynamic.csproj", "{AD4468F8-8883-434B-9D4C-E1801BB3B52A}" EndProject @@ -97,7 +97,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Cecil.Mdb", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.GenericMarshaler", "src\Java.Interop.GenericMarshaler\Java.Interop.GenericMarshaler.csproj", "{D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.BootstrapTasks", "src\Java.Interop.BootstrapTasks\Java.Interop.BootstrapTasks.csproj", "{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.BootstrapTasks", "build-tools\Java.Interop.BootstrapTasks\Java.Interop.BootstrapTasks.csproj", "{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Tools.AndroidSdk", "external\xamarin-android-tools\src\Xamarin.Android.Tools.AndroidSdk\Xamarin.Android.Tools.AndroidSdk.csproj", "{E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -349,6 +351,14 @@ Global {3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU {3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationRelease|Any CPU.ActiveCfg = Release|Any CPU {3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationRelease|Any CPU.Build.0 = Release|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.Release|Any CPU.Build.0 = Release|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.XAIntegrationRelease|Any CPU.ActiveCfg = Release|Any CPU + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}.XAIntegrationRelease|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {0C001D50-4176-45AE-BDC8-BA626508B0CC} = {C8F58966-94BF-407F-914A-8654F8B8AE3B} @@ -392,5 +402,6 @@ Global {C0487169-8F81-497F-919E-EB42B1D0243F} = {C8F58966-94BF-407F-914A-8654F8B8AE3B} {D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF} = {4C173212-371D-45D8-BA83-9226194F48DC} {3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A} = {172B608B-E6F3-41CC-9949-203A76BA247C} + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157} = {172B608B-E6F3-41CC-9949-203A76BA247C} EndGlobalSection EndGlobal diff --git a/Makefile b/Makefile index f8f278811..a45d39c62 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,9 @@ PACKAGES = \ packages/NUnit.3.7.1/NUnit.3.7.1.nupkg \ packages/NUnit.Console.3.7.0/NUnit.Console.3.7.0.nupkg +PREPARE_EXTERNAL_FILES = \ + external/xamarin-android-tools/src/Xamarin.Android.Tools.AndroidSdk/Xamarin.Android.Tools.AndroidSdk.csproj + DEPENDENCIES = \ bin/Test$(CONFIGURATION)/libNativeTiming$(NATIVE_EXT) @@ -55,16 +58,18 @@ run-all-tests: run-tests run-test-jnimarshal run-test-generator-core run-ptests include build-tools/scripts/msbuild.mk -prepare:: prepare-bootstrap prepare-external +prepare:: prepare-bootstrap -prepare-bootstrap: bin/Build$(CONFIGURATION)/Java.Interop.BootstrapTasks.dll +prepare-bootstrap: prepare-external bin/Build$(CONFIGURATION)/Java.Interop.BootstrapTasks.dll -bin/Build$(CONFIGURATION)/Java.Interop.BootstrapTasks.dll: src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj \ - $(wildcard src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/*.cs) - $(MSBUILD) $(MSBUILD_FLAGS) src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj +bin/Build$(CONFIGURATION)/Java.Interop.BootstrapTasks.dll: build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj \ + external/xamarin-android-tools/src/Xamarin.Android.Tools.AndroidSdk/Xamarin.Android.Tools.AndroidSdk.csproj \ + $(wildcard build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/*.cs) + $(MSBUILD) $(MSBUILD_FLAGS) "$<" -prepare-external: $(PACKAGES) $(NUNIT_CONSOLE) +prepare-external $(PREPARE_EXTERNAL_FILES): $(PACKAGES) $(NUNIT_CONSOLE) git submodule update --init --recursive + (cd external/xamarin-android-tools && $(MAKE) prepare) clean: -$(MSBUILD) $(MSBUILD_FLAGS) /t:Clean diff --git a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj similarity index 87% rename from src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj rename to build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj index 83567f08d..dbc5d407a 100644 --- a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj +++ b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks.csproj @@ -41,9 +41,14 @@ - + + + {E34BCFA0-CAA4-412C-AA1C-75DB8D67D157} + Xamarin.Android.Tools.AndroidSdk + + \ No newline at end of file diff --git a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/DownloadUri.cs b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/DownloadUri.cs similarity index 100% rename from src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/DownloadUri.cs rename to build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/DownloadUri.cs diff --git a/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/JdkInfo.cs b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/JdkInfo.cs new file mode 100644 index 000000000..889b6ca55 --- /dev/null +++ b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/JdkInfo.cs @@ -0,0 +1,122 @@ +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.Linq; + +using Xamarin.Android.Tools; + +using XATInfo = Xamarin.Android.Tools.JdkInfo; + +namespace Java.Interop.BootstrapTasks +{ + public class JdkInfo : Task + { + const string JARSIGNER = "jarsigner.exe"; + const string MDREG_KEY = @"SOFTWARE\Novell\Mono for Android"; + const string MDREG_JAVA_SDK = "JavaSdkDirectory"; + + public string JdksRoot { get; set; } + + public string MaximumJdkVersion { get; set; } + + static Regex VersionExtractor = new Regex (@"(?[\d]+(\.\d+)+)", RegexOptions.Compiled); + + [Required] + public ITaskItem PropertyFile { get; set; } + + [Required] + public ITaskItem MakeFragmentFile { get; set; } + + [Output] + public string JavaHomePath { get; set; } + + public override bool Execute () + { + var maxVersion = GetVersion (MaximumJdkVersion); + + XATInfo jdk = XATInfo.GetKnownSystemJdkInfos (CreateLogger ()) + .Where (j => maxVersion != null ? j.Version <= maxVersion : true) + .FirstOrDefault (); + + if (jdk == null) { + Log.LogError ("Could not determine JAVA_HOME location. Please set JdksRoot or export the JAVA_HOME environment variable."); + return false; + } + + JavaHomePath = jdk.HomePath; + + Directory.CreateDirectory (Path.GetDirectoryName (PropertyFile.ItemSpec)); + Directory.CreateDirectory (Path.GetDirectoryName (MakeFragmentFile.ItemSpec)); + + WritePropertyFile (jdk.JarPath, jdk.JavacPath, jdk.JdkJvmPath, jdk.IncludePath); + WriteMakeFragmentFile (jdk.JarPath, jdk.JavacPath, jdk.JdkJvmPath, jdk.IncludePath); + + return !Log.HasLoggedErrors; + } + + Version GetVersion (string value) + { + if (string.IsNullOrEmpty (value)) + return null; + if (!value.Contains (".")) { + value += ".0"; + } + Version v; + if (Version.TryParse (value, out v)) + return v; + return null; + } + + Action CreateLogger () + { + Action logger = (level, value) => { + switch (level) { + case TraceLevel.Error: + Log.LogError ("{0}", value); + break; + case TraceLevel.Warning: + Log.LogWarning ("{0}", value); + break; + default: + Log.LogMessage (MessageImportance.Low, "{0}", value); + break; + } + }; + return logger; + } + + void WritePropertyFile (string jarPath, string javacPath, string jdkJvmPath, IEnumerable includes) + { + var msbuild = XNamespace.Get ("http://schemas.microsoft.com/developer/msbuild/2003"); + var project = new XElement (msbuild + "Project", + new XElement (msbuild + "Choose", + new XElement (msbuild + "When", new XAttribute ("Condition", " '$(JdkJvmPath)' == '' "), + new XElement (msbuild + "PropertyGroup", + new XElement (msbuild + "JdkJvmPath", jdkJvmPath)), + new XElement (msbuild + "ItemGroup", + includes.Select (i => new XElement (msbuild + "JdkIncludePath", new XAttribute ("Include", i)))))), + new XElement (msbuild + "PropertyGroup", + new XElement (msbuild + "JavaCPath", new XAttribute ("Condition", " '$(JavaCPath)' == '' "), + javacPath), + new XElement (msbuild + "JarPath", new XAttribute ("Condition", " '$(JarPath)' == '' "), + jarPath))); + project.Save (PropertyFile.ItemSpec); + } + + void WriteMakeFragmentFile (string jarPath, string javacPath, string jdkJvmPath, IEnumerable includes) + { + using (var o = new StreamWriter (MakeFragmentFile.ItemSpec)) { + o.WriteLine ($"export JI_JAR_PATH := {jarPath}"); + o.WriteLine ($"export JI_JAVAC_PATH := {javacPath}"); + o.WriteLine ($"export JI_JDK_INCLUDE_PATHS := {string.Join (" ", includes)}"); + o.WriteLine ($"export JI_JVM_PATH := {jdkJvmPath}"); + } + } + } +} diff --git a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/OS.cs b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/OS.cs similarity index 58% rename from src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/OS.cs rename to build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/OS.cs index 585c20ec5..4e9d11a4f 100644 --- a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/OS.cs +++ b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/OS.cs @@ -6,17 +6,5 @@ class OS { public static readonly bool IsWindows = Path.DirectorySeparatorChar == '\\'; public static readonly bool IsMacOS = !IsWindows && Directory.Exists ("/Applications"); public static readonly bool IsLinux = !IsWindows && !IsMacOS; - - public static readonly string NativeLibraryFormat; - - static OS () - { - if (IsWindows) - NativeLibraryFormat = "{0}.dll"; - if (IsMacOS) - NativeLibraryFormat = "lib{0}.dylib"; - if (IsLinux) - NativeLibraryFormat = "lib{0}.so"; - } } } diff --git a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/SetEnvironmentVariable.cs b/build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/SetEnvironmentVariable.cs similarity index 100% rename from src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/SetEnvironmentVariable.cs rename to build-tools/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/SetEnvironmentVariable.cs diff --git a/src/Java.Interop.BootstrapTasks/Properties/AssemblyInfo.cs b/build-tools/Java.Interop.BootstrapTasks/Properties/AssemblyInfo.cs similarity index 100% rename from src/Java.Interop.BootstrapTasks/Properties/AssemblyInfo.cs rename to build-tools/Java.Interop.BootstrapTasks/Properties/AssemblyInfo.cs diff --git a/build-tools/scripts/PrepareWindows.targets b/build-tools/scripts/PrepareWindows.targets index 7cbba368f..29b13021a 100644 --- a/build-tools/scripts/PrepareWindows.targets +++ b/build-tools/scripts/PrepareWindows.targets @@ -12,7 +12,7 @@ - + [\d]+(\.\d+)+)", RegexOptions.Compiled); - - [Required] - public ITaskItem PropertyFile { get; set; } - - [Required] - public ITaskItem MakeFragmentFile { get; set; } - - [Output] - public string JavaHomePath { get; set; } - - public override bool Execute () - { - Log.LogMessage (MessageImportance.Low, $"Task {nameof (JdkInfo)}"); - Log.LogMessage (MessageImportance.Low, $" {nameof (JdksRoot)}: {JdksRoot}"); - Log.LogMessage (MessageImportance.Low, $" {nameof (MakeFragmentFile)}: {MakeFragmentFile}"); - Log.LogMessage (MessageImportance.Low, $" {nameof (MaximumJdkVersion)}: {MaximumJdkVersion}"); - Log.LogMessage (MessageImportance.Low, $" {nameof (PropertyFile)}: {PropertyFile}"); - - var maxVersion = GetVersion (MaximumJdkVersion); - - string jarPath = null; - string javacPath = null; - string jdkJvmPath = null; - string includePath = null; - - var java_home = GetJavaHomePathFromEnvironment (); - if (!ValidateJdkPath (maxVersion, java_home, out jarPath, out javacPath, out jdkJvmPath, out includePath)) { - java_home = null; - } - - if (java_home == null && - (java_home = GetJavaHomePathFromLibexec ()) != null && - !ValidateJdkPath (maxVersion, java_home, out jarPath, out javacPath, out jdkJvmPath, out includePath)) { - java_home = null; - } - - if (java_home == null && - (java_home = GetJavaHomePathFromAlternatives ()) != null && - !ValidateJdkPath (maxVersion, java_home, out jarPath, out javacPath, out jdkJvmPath, out includePath)) { - java_home = null; - } - - if (java_home == null && - (java_home = GetJavaHomePathFromMachine (maxVersion)) != null && - !ValidateJdkPath (maxVersion, java_home, out jarPath, out javacPath, out jdkJvmPath, out includePath)) { - java_home = null; - } - - if (java_home == null) { - Log.LogError ("Could not determine JAVA_HOME location. Please set JdksRoot or export the JAVA_HOME environment variable."); - return false; - } - - var includes = new List () { - includePath, - }; - includes.AddRange (Directory.GetDirectories (includePath)); - - if (Log.HasLoggedErrors) { - return false; - } - - JavaHomePath = java_home; - - Directory.CreateDirectory (Path.GetDirectoryName (PropertyFile.ItemSpec)); - Directory.CreateDirectory (Path.GetDirectoryName (MakeFragmentFile.ItemSpec)); - - WritePropertyFile (jarPath, javacPath, jdkJvmPath, includes); - WriteMakeFragmentFile (jarPath, javacPath, jdkJvmPath, includes); - - Log.LogMessage (MessageImportance.Low, $" [Output] {nameof (JavaHomePath)}: {JavaHomePath}"); - - return !Log.HasLoggedErrors; - } - - Version GetVersion (string value) - { - if (string.IsNullOrEmpty (value)) - return null; - if (!value.Contains (".")) { - value += ".0"; - } - Version v; - if (Version.TryParse (value, out v)) - return v; - return null; - } - - Version GetVersionFromPath (string path) - { - var m = VersionExtractor.Match (path); - if (!m.Success) - return null; - return GetVersion (m.Groups ["version"].Value); - } - - void FileExists (string path) - { - if (!File.Exists (path)) { - var name = Path.GetFileName (path); - Log.LogError ($"Could not determine location of `{name}`; tried `{path}`."); - } - } - - void WritePropertyFile (string jarPath, string javacPath, string jdkJvmPath, IEnumerable includes) - { - var msbuild = XNamespace.Get ("http://schemas.microsoft.com/developer/msbuild/2003"); - var project = new XElement (msbuild + "Project", - new XElement (msbuild + "Choose", - new XElement (msbuild + "When", new XAttribute ("Condition", " '$(JdkJvmPath)' == '' "), - new XElement (msbuild + "PropertyGroup", - new XElement (msbuild + "JdkJvmPath", jdkJvmPath)), - new XElement (msbuild + "ItemGroup", - includes.Select (i => new XElement (msbuild + "JdkIncludePath", new XAttribute ("Include", i)))))), - new XElement (msbuild + "PropertyGroup", - new XElement (msbuild + "JavaCPath", new XAttribute ("Condition", " '$(JavaCPath)' == '' "), - javacPath), - new XElement (msbuild + "JarPath", new XAttribute ("Condition", " '$(JarPath)' == '' "), - jarPath))); - project.Save (PropertyFile.ItemSpec); - } - - void WriteMakeFragmentFile (string jarPath, string javacPath, string jdkJvmPath, IEnumerable includes) - { - using (var o = new StreamWriter (MakeFragmentFile.ItemSpec)) { - o.WriteLine ($"export JI_JAR_PATH := {jarPath}"); - o.WriteLine ($"export JI_JAVAC_PATH := {javacPath}"); - o.WriteLine ($"export JI_JDK_INCLUDE_PATHS := {string.Join (" ", includes)}"); - o.WriteLine ($"export JI_JVM_PATH := {jdkJvmPath}"); - } - } - - bool ValidateJdkPath (Version maxVersion, string java_home) - { - return ValidateJdkPath (maxVersion, java_home, - out _, out _, out _, out _); - } - - bool ValidateJdkPath (Version maxVersion, string java_home, - out string jarPath, out string javacPath, out string jdkJvmPath, out string includePath) - { - jarPath = javacPath = jdkJvmPath = includePath = null; - - if (string.IsNullOrEmpty (java_home) || !Directory.Exists (java_home)) - return false; - - var pathVersion = GetVersionFromPath (java_home); - if (maxVersion != null && pathVersion != null && pathVersion > maxVersion) { - Log.LogMessage (MessageImportance.Low, - $" Skipping JAVA_HOME value of `{java_home}` as it exceeds MaximumJdkVersion={MaximumJdkVersion}."); - return false; - } - - jarPath = FindExecutablesInDirectory (Path.Combine (java_home, "bin"), "jar").FirstOrDefault (); - javacPath = FindExecutablesInDirectory (Path.Combine (java_home, "bin"), "javac").FirstOrDefault (); - var jdkJvmPaths = OS.IsMacOS - ? FindLibrariesInDirectory (java_home, "jli") - : FindLibrariesInDirectory (Path.Combine (java_home, "jre"), "jvm"); - jdkJvmPath = jdkJvmPaths.FirstOrDefault (); - includePath = Path.Combine (java_home, "include"); - - if (jarPath == null) { - Log.LogMessage (MessageImportance.Low, $" Skipping JAVA_HOME value of `{java_home}` as `jar` could not be found."); - return false; - } - if (javacPath == null) { - Log.LogMessage (MessageImportance.Low, $" Skipping JAVA_HOME value of `{java_home}` as `javac` could not be found."); - return false; - } - if (jdkJvmPath == null) { - var jvm = OS.IsMacOS ? "libjli.dylib" : string.Format (OS.NativeLibraryFormat, "jvm"); - Log.LogMessage (MessageImportance.Low, $" Skipping JAVA_HOME value of `{java_home}` as `{jvm} could not be found."); - return false; - } - if (!Directory.Exists (includePath)) { - Log.LogMessage (MessageImportance.Low, $" Skipping JAVA_HOME value of `{java_home}` as the `include` directory could not be found."); - return false; - } - - return true; - } - - string GetJavaHomePathFromEnvironment () - { - var java_home = Environment.GetEnvironmentVariable ("JAVA_HOME"); - if (!string.IsNullOrEmpty (java_home)) - return java_home; - return null; - } - - // macOS - string GetJavaHomePathFromLibexec () - { - var java_home = Path.GetFullPath ("/usr/libexec/java_home"); - if (!File.Exists (java_home)) { - return null; - } - string path = null; - Exec (java_home, "", (o, e) => { - if (string.IsNullOrEmpty (e.Data)) - return; - Log.LogMessage (MessageImportance.Low, $" {e.Data}"); - path = e.Data; - }); - return path; - } - - // Linux - string GetJavaHomePathFromAlternatives () - { - var alternatives = Path.GetFullPath ("/etc/alternatives/java"); - if (!File.Exists (alternatives)) - return null; - string targetJava = null; - Exec ("readlink", $"\"{alternatives}\"", (o, e) => { - if (string.IsNullOrEmpty (e.Data)) - return; - Log.LogMessage (MessageImportance.Low, $" {e.Data}"); - targetJava = e.Data; - }); - if (string.IsNullOrEmpty (targetJava)) - return null; - return GetJavaHomePathFromJava (targetJava); - } - - string GetJavaHomePathFromMachine (Version maxVersion) - { - var java_homes = GetJavaHomePathsFromDirectory (JdksRoot) - .Concat (GetJavaHomePathsFromJava ()) - .Concat (GetJavaHomePathsFromWindowsRegistry ()) - .Where (d => Directory.Exists (d)) - .Distinct () - .ToList (); - - foreach (var p in java_homes) { - Log.LogMessage (MessageImportance.Low, $" Possible JAVA_HOME location: {p}"); - } - - var versionComparer = new ComparisonComparer((x, y) => { - int r = 0; - if (x.Version != null && y.Version != null) - r = x.Version.CompareTo (y.Version); - return r; - }); - - java_homes = java_homes.Where (d => ValidateJdkPath (maxVersion, d)) - .Select (jh => new JdkComparisonInfo { - Path = jh, - Version = GetVersionFromPath (jh), - }) - .Where (v => (maxVersion == null || v.Version == null) ? true : v.Version <= maxVersion) - .OrderByDescending (v => v, versionComparer) - .ThenByDescending (v => Directory.GetLastWriteTimeUtc (v.Path)) - .Select (v => v.Path) - .ToList (); - - foreach (var p in java_homes) { - Log.LogMessage (MessageImportance.Low, $" Filtered JAVA_HOME location: {p}"); - } - - return java_homes.FirstOrDefault (); - } - - IEnumerable GetJavaHomePathsFromDirectory (string jdksRoot) - { - if (string.IsNullOrEmpty (jdksRoot)) - yield break; - if (!Directory.Exists (jdksRoot)) - yield break; - foreach (var d in Directory.EnumerateDirectories (jdksRoot)) { - var h = d; - if (OS.IsMacOS) - h = Path.Combine (h, "Contents", "Home"); - yield return h; - } - } - - IEnumerable GetJavaHomePathsFromJava () - { - var javas = Environment.GetEnvironmentVariable ("PATH").Split (Path.PathSeparator) - .SelectMany (p => FindExecutablesInDirectory (p, "java")); - - foreach (var exe in javas) { - var java_home = GetJavaHomePathFromJava (exe); - if (string.IsNullOrEmpty (java_home)) - continue; - yield return java_home; - } - } - - string GetJavaHomePathFromJava (string java) - { - const string JavaHome = "java.home = "; - string java_home = null; - Exec (java, "-XshowSettings:properties -version", (o, e) => { - int i = e.Data?.IndexOf (JavaHome) ?? -1; - if (i < 0) - return; - Log.LogMessage (MessageImportance.Low, $" {e.Data}"); - java_home = e.Data.Substring (JavaHome.Length + i); - // `java -XshowSettings:properties -version 2>&1 | grep java.home` ends with `/jre` on macOS. - // We need the parent dir so we can properly lookup the `include` directories - if (java_home.EndsWith ("jre", StringComparison.OrdinalIgnoreCase)) { - java_home = Path.GetDirectoryName (java_home); - } - }); - return java_home; - } - - void Exec (string java, string arguments, DataReceivedEventHandler output) - { - Log.LogMessage (MessageImportance.Low, $" Tool {java} execution started with arguments: {arguments}"); - var psi = new ProcessStartInfo () { - FileName = java, - Arguments = arguments, - UseShellExecute = false, - RedirectStandardInput = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - }; - var p = new Process () { - StartInfo = psi, - }; - p.OutputDataReceived += output; - p.ErrorDataReceived += output; - - using (p) { - p.StartInfo = psi; - p.Start (); - p.BeginOutputReadLine (); - p.BeginErrorReadLine (); - p.WaitForExit (); - } - } - - IEnumerable GetJavaHomePathsFromWindowsRegistry () - { - if (Path.DirectorySeparatorChar == '/') - yield break; - - // 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); - } - - string subkey = @"SOFTWARE\JavaSoft\Java Development Kit"; - - Log.LogMessage (MessageImportance.Low, "Looking for Java 6 SDK..."); - - 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)) { - Log.LogMessage (MessageImportance.Low, $" Key {key_name} found."); - - // 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); - } - - Log.LogMessage (MessageImportance.Low, $" Key {key_name} not found."); - } - } - - string GetMDRegistryKey () - { - var regKey = Environment.GetEnvironmentVariable ("XAMARIN_ANDROID_REGKEY"); - return string.IsNullOrWhiteSpace (regKey) ? MDREG_KEY : regKey; - } - - private bool CheckRegistryKeyForExecutable (UIntPtr key, string subkey, string valueName, RegistryEx.Wow64 wow64, string subdir, string exe) - { - string key_name = string.Format (@"{0}\{1}\{2}", key == RegistryEx.CurrentUser ? "HKCU" : "HKLM", subkey, valueName); - - var value = RegistryEx.GetValueString (key, subkey, valueName, wow64); - var path = string.IsNullOrEmpty (value) ? null : value; - - if (path == null) { - Log.LogMessage (MessageImportance.Low, $" Key {key_name} not found."); - return false; - } - - if (!FindExecutablesInDirectory (Path.Combine (path, subdir), exe).Any ()) { - Log.LogMessage (MessageImportance.Low, $" Key {key_name} found:\n Path does not contain {exe} in \\{subdir} ({path})."); - return false; - } - - Log.LogMessage (MessageImportance.Low, $" Key {key_name} found:\n Path contains {exe} in \\{subdir} ({path})."); - - return true; - } - - IEnumerable FindExecutablesInDirectory (string dir, string executable) - { - foreach (var exe in Executables (executable)) { - var p = Path.Combine (dir, exe); - if (File.Exists (p)) - yield return p; - } - } - - IEnumerable Executables (string executable) - { - var pathExt = Environment.GetEnvironmentVariable ("PATHEXT"); - var pathExts = pathExt?.Split (new char [] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); - - if (pathExts == null) { - yield return executable; - yield break; - } - - foreach (var ext in pathExts) - yield return Path.ChangeExtension (executable, ext); - } - - IEnumerable FindLibrariesInDirectory (string dir, string libraryName) - { - var library = string.Format (OS.NativeLibraryFormat, libraryName); - return Directory.EnumerateFiles (dir, library, SearchOption.AllDirectories); - } - - class JdkComparisonInfo { - public string Path; - public Version Version; - } - } - - class ComparisonComparer : IComparer { - - Comparison comparison; - - public ComparisonComparer (Comparison comparison) - { - this.comparison = comparison; - } - - public int Compare (T x, T y) - { - return comparison (x, y); - } - } -} diff --git a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/RegistryEx.cs b/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/RegistryEx.cs deleted file mode 100644 index 0973a7716..000000000 --- a/src/Java.Interop.BootstrapTasks/Java.Interop.BootstrapTasks/RegistryEx.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace Java.Interop.BootstrapTasks -{ - internal static class RegistryEx - { - const string ADVAPI = "advapi32.dll"; - - public static UIntPtr CurrentUser = (UIntPtr)0x80000001; - public static UIntPtr LocalMachine = (UIntPtr)0x80000002; - - [DllImport (ADVAPI, CharSet = CharSet.Unicode, SetLastError = true)] - static extern int RegOpenKeyEx (UIntPtr hKey, string subKey, uint reserved, uint sam, out UIntPtr phkResult); - - [DllImport (ADVAPI, CharSet = CharSet.Unicode, SetLastError = true)] - static extern int RegQueryValueExW (UIntPtr hKey, string lpValueName, int lpReserved, out uint lpType, - StringBuilder lpData, ref uint lpcbData); - - [DllImport (ADVAPI, CharSet = CharSet.Unicode, SetLastError = true)] - static extern int RegSetValueExW (UIntPtr hKey, string lpValueName, int lpReserved, - uint dwType, string data, uint cbData); - - [DllImport (ADVAPI, CharSet = CharSet.Unicode, SetLastError = true)] - static extern int RegSetValueExW (UIntPtr hKey, string lpValueName, int lpReserved, - uint dwType, IntPtr data, uint cbData); - - [DllImport (ADVAPI, CharSet = CharSet.Unicode, SetLastError = true)] - static extern int RegCreateKeyEx (UIntPtr hKey, string subKey, uint reserved, string @class, uint options, - uint samDesired, IntPtr lpSecurityAttributes, out UIntPtr phkResult, out Disposition lpdwDisposition); - - [DllImport ("advapi32.dll", SetLastError = true)] - static extern int RegCloseKey (UIntPtr hKey); - - public static string GetValueString (UIntPtr key, string subkey, string valueName, Wow64 wow64) - { - UIntPtr regKeyHandle; - uint sam = (uint)Rights.QueryValue + (uint)wow64; - if (RegOpenKeyEx (key, subkey, 0, sam, out regKeyHandle) != 0) - return null; - - try { - uint type; - var sb = new StringBuilder (2048); - uint cbData = (uint)sb.Capacity; - if (RegQueryValueExW (regKeyHandle, valueName, 0, out type, sb, ref cbData) == 0) { - return sb.ToString (); - } - return null; - } finally { - RegCloseKey (regKeyHandle); - } - } - - public static void SetValueString (UIntPtr key, string subkey, string valueName, string value, Wow64 wow64) - { - UIntPtr regKeyHandle; - uint sam = (uint)(Rights.CreateSubKey | Rights.SetValue) + (uint)wow64; - uint options = (uint)Options.NonVolatile; - Disposition disposition; - if (RegCreateKeyEx (key, subkey, 0, null, options, sam, IntPtr.Zero, out regKeyHandle, out disposition) != 0) { - throw new Exception ("Could not open or craete key"); - } - - try { - uint type = (uint)ValueType.String; - uint lenBytesPlusNull = ((uint)value.Length + 1) * 2; - var result = RegSetValueExW (regKeyHandle, valueName, 0, type, value, lenBytesPlusNull); - if (result != 0) - throw new Exception (string.Format ("Error {0} setting registry key '{1}{2}@{3}'='{4}'", - result, key, subkey, valueName, value)); - } finally { - RegCloseKey (regKeyHandle); - } - } - - [Flags] - enum Rights : uint - { - None = 0, - QueryValue = 0x0001, - SetValue = 0x0002, - CreateSubKey = 0x0004, - EnumerateSubKey = 0x0008, - } - - enum Options - { - BackupRestore = 0x00000004, - CreateLink = 0x00000002, - NonVolatile = 0x00000000, - Volatile = 0x00000001, - } - - public enum Wow64 : uint - { - Key64 = 0x0100, - Key32 = 0x0200, - } - - enum ValueType : uint - { - None = 0, //REG_NONE - String = 1, //REG_SZ - UnexpandedString = 2, //REG_EXPAND_SZ - Binary = 3, //REG_BINARY - DWord = 4, //REG_DWORD - DWordLittleEndian = 4, //REG_DWORD_LITTLE_ENDIAN - DWordBigEndian = 5, //REG_DWORD_BIG_ENDIAN - Link = 6, //REG_LINK - MultiString = 7, //REG_MULTI_SZ - ResourceList = 8, //REG_RESOURCE_LIST - FullResourceDescriptor = 9, //REG_FULL_RESOURCE_DESCRIPTOR - ResourceRequirementsList = 10, //REG_RESOURCE_REQUIREMENTS_LIST - QWord = 11, //REG_QWORD - QWordLittleEndian = 11, //REG_QWORD_LITTLE_ENDIAN - } - - enum Disposition : uint - { - CreatedNewKey = 0x00000001, - OpenedExistingKey = 0x00000002, - } - } -}