From 759c1ddedc09a4670c986bf1f842acd8a57122f4 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 5 Jan 2021 22:01:12 +0100 Subject: [PATCH] Update to NDK r22 Context: https://github.com/android/ndk/wiki/Changelog-r22 The most important NDK changes: * GNU binutils are deprecated (but still used) * LLVM 11 is used for the toolchain * LLD is now the default linker XA changes: * All the binutils tools are listed in a single location, `Configurables.Defaults`, now * Host NDK binutils are installed by xaprepare instead of by Xamarin.Android.Build.Tools * `UBSAN` checked builds require `RTTI` and exceptions now --- .external | 2 +- build-tools/cmake/xa_common.cmake | 10 +- .../xaprepare/Application/BuildInfo.cs | 44 +- .../xaprepare/Application/KnownProperties.cs | 1 + .../xaprepare/Application/NDKTool.cs | 22 + .../Application/Properties.Defaults.cs.in | 1 + .../ConfigAndData/BuildAndroidPlatforms.cs | 4 +- .../xaprepare/ConfigAndData/Configurables.cs | 17 +- .../xaprepare/OperatingSystems/OS.cs | 5 + .../xaprepare/OperatingSystems/Windows.cs | 10 + .../xaprepare/Scenarios/Scenario_Standard.cs | 4 + .../xaprepare/Steps/Step_Android_SDK_NDK.cs | 17 +- .../Steps/Step_Get_Windows_Binutils.cs | 76 +- .../Steps/Step_InstallNDKBinutils.cs | 50 ++ .../xaprepare/xaprepare/xaprepare.targets | 1 + .../ndk/android.toolchain.cmake.ndk_r21.3 | 726 ++++++++++++++++++ src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs | 28 +- .../Tasks/NdkUtils.cs | 113 ++- .../Xamarin.Android.Build.Tests/AotTests.cs | 12 +- .../Tasks/NdkUtilTests.cs | 2 +- .../Xamarin.Android.Build.Tasks.targets | 62 -- src/monodroid/CMakeLists.txt | 18 +- src/monodroid/monodroid.targets | 4 +- src/sqlite-xamarin/CMakeLists.txt | 2 +- 24 files changed, 1084 insertions(+), 147 deletions(-) create mode 100644 build-tools/xaprepare/xaprepare/Application/NDKTool.cs create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_InstallNDKBinutils.cs create mode 100644 src-ThirdParty/ndk/android.toolchain.cmake.ndk_r21.3 diff --git a/.external b/.external index bfd75f844ef..ab12b96f077 100644 --- a/.external +++ b/.external @@ -1,2 +1,2 @@ xamarin/monodroid:main@7da768cf9ddbd137bbce5326dab79b139bcc59e0 -mono/mono:2020-02@5e9cb6d1c1de430965312927d5aed7fcb27bfa73 +mono/mono:2020-02@c66141a8c7ba2566c578c2dd012b2b723e006213 diff --git a/build-tools/cmake/xa_common.cmake b/build-tools/cmake/xa_common.cmake index a7acdc56b92..e67b2ccd2ca 100644 --- a/build-tools/cmake/xa_common.cmake +++ b/build-tools/cmake/xa_common.cmake @@ -1,9 +1,3 @@ -if((CMAKE_VERSION_MAJOR EQUAL 3 AND CMAKE_VERSION_MINOR GREATER_EQUAL 7) OR CMAKE_VERSION_MAJOR GREATER 3) - set(CMAKE_POLICY_DEFAULT_CMP0066 NEW) -endif() - -if((CMAKE_VERSION_MAJOR EQUAL 3 AND CMAKE_VERSION_MINOR GREATER_EQUAL 8) OR CMAKE_VERSION_MAJOR GREATER 3) - set(CMAKE_POLICY_DEFAULT_CMP0067 NEW) -endif() - +set(CMAKE_POLICY_DEFAULT_CMP0066 NEW) +set(CMAKE_POLICY_DEFAULT_CMP0067 NEW) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12) diff --git a/build-tools/xaprepare/xaprepare/Application/BuildInfo.cs b/build-tools/xaprepare/xaprepare/Application/BuildInfo.cs index 5184919c828..69b8aed2e48 100644 --- a/build-tools/xaprepare/xaprepare/Application/BuildInfo.cs +++ b/build-tools/xaprepare/xaprepare/Application/BuildInfo.cs @@ -10,7 +10,6 @@ namespace Xamarin.Android.Prepare partial class BuildInfo : AppObject { static readonly char[] NDKPropertySeparator = new [] { '=' }; - static readonly char[] NDKPlatformDirectorySeparator = new [] { '-' }; public string CommitOfLastVersionChange { get; private set; } = String.Empty; @@ -38,9 +37,10 @@ public async Task GatherGitInfo (Context context) Log.StatusLine (); } - public bool GatherNDKInfo (Context context, string ndkRoot) + public bool GatherNDKInfo (Context context) { - string props = Path.Combine (ndkRoot, "source.properties"); + string ndkDir = Configurables.Paths.AndroidNdkDirectory; + string props = Path.Combine (ndkDir, "source.properties"); if (!File.Exists (props)) { Log.ErrorLine ("NDK properties file does not exist: ", props, tailColor: Log.DestinationColor); return false; @@ -71,24 +71,32 @@ public bool GatherNDKInfo (Context context, string ndkRoot) break; } + Log.DebugLine ($"Looking for minimum API available in {ndkDir}"); int minimumApi = Int32.MaxValue; - string platforms = Path.Combine (ndkRoot, "platforms"); - foreach (string p in Directory.EnumerateDirectories (platforms, "android-*", SearchOption.TopDirectoryOnly)) { - string pdir = Path.GetFileName (p); - string[] parts = pdir.Split (NDKPlatformDirectorySeparator, 2); - if (parts.Length != 2) - continue; - - int api; - if (!Int32.TryParse (parts [1].Trim (), out api)) - continue; - - if (api >= minimumApi) - continue; - - minimumApi = api; + foreach (var kvp in Configurables.Defaults.AndroidToolchainPrefixes) { + string dirName = kvp.Value; + string platforms = Path.Combine (Configurables.Paths.AndroidToolchainSysrootLibDirectory, dirName); + Log.DebugLine ($" searching in {platforms}"); + foreach (string p in Directory.EnumerateDirectories (platforms, "*", SearchOption.TopDirectoryOnly)) { + string plibc = Path.Combine (p, "libc.so"); + if (!Utilities.FileExists (plibc)) { + continue; + } + + Log.DebugLine ($" found {p}"); + string pdir = Path.GetFileName (p); + int api; + if (!Int32.TryParse (pdir, out api)) + continue; + + if (api >= minimumApi) + continue; + + minimumApi = api; + } } + Log.DebugLine ($"Detected minimum NDK API level: {minimumApi}"); NDKMinimumApiAvailable = minimumApi.ToString (); return true; } diff --git a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs index 0ba5b333a36..73a557c9441 100644 --- a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs +++ b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs @@ -22,6 +22,7 @@ static class KnownProperties public const string DotNetPreviewVersionFull = "DotNetPreviewVersionFull"; public const string EmulatorVersion = "EmulatorVersion"; public const string EmulatorPkgRevision = "EmulatorPkgRevision"; + public const string HostOS = "HostOS"; public const string IgnoreMaxMonoVersion = "IgnoreMaxMonoVersion"; public const string IgnoreMinMonoVersion = "IgnoreMinMonoVersion"; public const string JavaInteropFullPath = "JavaInteropFullPath"; diff --git a/build-tools/xaprepare/xaprepare/Application/NDKTool.cs b/build-tools/xaprepare/xaprepare/Application/NDKTool.cs new file mode 100644 index 00000000000..daf9f9e53c6 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Application/NDKTool.cs @@ -0,0 +1,22 @@ +using System; + +namespace Xamarin.Android.Prepare +{ + class NDKTool + { + public string Name { get; } + public string DestinationName { get; } = String.Empty; + + public NDKTool (string name, string? destinationName = null) + { + if (name.Trim ().Length == 0) { + throw new ArgumentException (nameof (name), "must not be empty"); + } + Name = name; + if (String.IsNullOrWhiteSpace (destinationName)) { + return; + } + DestinationName = destinationName!; + } + } +} diff --git a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in index 589aa03cb82..c440ef06124 100644 --- a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in +++ b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in @@ -26,6 +26,7 @@ namespace Xamarin.Android.Prepare properties.Add (KnownProperties.DotNetPreviewVersionFull, StripQuotes ("@DotNetPreviewVersionFull@")); properties.Add (KnownProperties.EmulatorVersion, StripQuotes ("@EmulatorVersion@")); properties.Add (KnownProperties.EmulatorPkgRevision, StripQuotes ("@EmulatorPkgRevision@")); + properties.Add (KnownProperties.HostOS, StripQuotes ("@HostOS@")); properties.Add (KnownProperties.IgnoreMaxMonoVersion, StripQuotes ("@IgnoreMaxMonoVersion@")); properties.Add (KnownProperties.IgnoreMinMonoVersion, StripQuotes ("@IgnoreMinMonoVersion@")); properties.Add (KnownProperties.JavaInteropFullPath, StripQuotes (@"@JavaInteropFullPath@")); diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs index f4bd391098b..0121caed26b 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -5,8 +5,8 @@ namespace Xamarin.Android.Prepare { class BuildAndroidPlatforms { - public const string AndroidNdkVersion = "21d"; - public const string AndroidNdkPkgRevision = "21.3.6528147"; + public const string AndroidNdkVersion = "22"; + public const string AndroidNdkPkgRevision = "22.0.7026061"; public static readonly List AllPlatforms = new List { new AndroidPlatform (apiName: "", apiLevel: 1, platformID: "1"), diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs index 74569f27352..6c9e108d4a6 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs @@ -266,6 +266,12 @@ public static partial class Defaults /// public static readonly List BuildStatusBundleExclude = new List { }; + + public static readonly List NDKTools = new List { + new NDKTool (name: "as"), + new NDKTool (name: "ld.gold", destinationName: "ld"), + new NDKTool (name: "strip"), + }; } public static partial class Paths @@ -362,7 +368,12 @@ public static partial class Paths public static string MonoArchiveWindowsLocalPath => Path.Combine (ctx.Properties.GetRequiredValue (KnownProperties.AndroidToolchainCacheDirectory), MonoArchiveWindowsFileName); // Other - public static string AndroidToolchainBinDirectory => EnsureAndroidToolchainBinDirectories (); + public static string AndroidNdkDirectory => ctx.Properties.GetRequiredValue (KnownProperties.AndroidNdkDirectory); + public static string AndroidToolchainRootDirectory => GetCachedPath (ref androidToolchainRootDirectory, () => Path.Combine (AndroidNdkDirectory, "toolchains", "llvm", "prebuilt", NdkToolchainOSTag)); + public static string AndroidToolchainBinDirectory => GetCachedPath (ref androidToolchainBinDirectory, () => Path.Combine (AndroidToolchainRootDirectory, "bin")); + public static string AndroidToolchainSysrootLibDirectory => GetCachedPath (ref androidToolchainSysrootLibDirectory, () => Path.Combine (AndroidToolchainRootDirectory, "sysroot", "usr", "lib")); + public static string WindowsBinutilsInstallDir => GetCachedPath (ref windowsBinutilsInstallDir, () => Path.Combine (InstallMSBuildDir, "ndk")); + public static string HostBinutilsInstallDir => GetCachedPath (ref hostBinutilsInstallDir, () => Path.Combine (InstallMSBuildDir, ctx.Properties.GetRequiredValue (KnownProperties.HostOS), "ndk")); // not really configurables, merely convenience aliases for more frequently used paths that come from properties public static string XAInstallPrefix => ctx.Properties.GetRequiredValue (KnownProperties.XAInstallPrefix); @@ -410,7 +421,9 @@ static string GetCachedPath (ref string? variable, Func creator) static string? binDir; static string? netCoreBinDir; static string? monoSDKsOutputDir; + static string? androidToolchainRootDirectory; static string? androidToolchainBinDirectory; + static string? androidToolchainSysrootLibDirectory; static string? monoProfileDir; static string? monoProfileToolsDir; static string? bclTestsDestDir; @@ -449,6 +462,8 @@ static string GetCachedPath (ref string? variable, Func creator) static string? openJDK8CacheDir, openJDK11CacheDir; static string? oldOpenJDKInstallDir; static string? configurationPropsGeneratedPath; + static string? windowsBinutilsInstallDir; + static string? hostBinutilsInstallDir; } } } diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs index 815f1cfcbf2..103eb853b80 100644 --- a/build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/OS.cs @@ -521,6 +521,11 @@ string GetExecutableWithExtension (string programPath, Func find return String.Empty; } + public virtual string AppendExecutableExtension (string programName) + { + return programName; + } + protected static string FindProgram (string programName, List directories) { foreach (string dir in directories) { diff --git a/build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs b/build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs index 0e059ff7723..06ee050dca0 100644 --- a/build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs +++ b/build-tools/xaprepare/xaprepare/OperatingSystems/Windows.cs @@ -80,6 +80,16 @@ public override string Which (string programPath, bool required = true) return base.Which (programPath, required); } + public override string AppendExecutableExtension (string programName) + { + string ext = Path.GetExtension (programName); + if (String.Compare (".exe", ext, StringComparison.OrdinalIgnoreCase) == 0) { + return programName; + } + + return $"{programName}.exe"; + } + protected override string AssertIsExecutable (string fullPath) { return fullPath; diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs index 3a0f196c432..072b02f9a6d 100644 --- a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs @@ -31,6 +31,10 @@ protected override void AddSteps (Context context) Steps.Add (new Step_DownloadMonoArchive ()); AddRequiredOSSpecificSteps (true); Steps.Add (new Step_InstallMonoRuntimes ()); + + // The next two steps MUST be after InstallMonoRuntimes above since the latter cleans up the target + // directory where the NDK binutils are installed + Steps.Add (new Step_InstallNDKBinutils ()); Steps.Add (new Step_Get_Windows_Binutils ()); Steps.Add (new Step_GenerateCGManifest ()); diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs b/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs index cf85ff95e81..f7a19e0f27f 100644 --- a/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs +++ b/build-tools/xaprepare/xaprepare/Steps/Step_Android_SDK_NDK.cs @@ -55,7 +55,7 @@ protected override async Task Execute (Context context) return false; } WritePackageXmls (sdkRoot); - return GatherNDKInfo (context, ndkRoot); + return GatherNDKInfo (context); } Log.MessageLine (); @@ -101,7 +101,7 @@ protected override async Task Execute (Context context) WritePackageXmls (sdkRoot); - return GatherNDKInfo (context, ndkRoot); + return GatherNDKInfo (context); } bool AcceptLicenses (Context context, string sdkRoot) @@ -145,13 +145,22 @@ bool AcceptLicenses (Context context, string sdkRoot) return true; } - bool GatherNDKInfo (Context context, string ndkRoot) + bool GatherNDKInfo (Context context) { + if (context.OS.IsWindows) { + // Quick hack to test https://github.com/android/ndk/issues/1427#issuecomment-763424992 + Log.Info ("Copying NDK r21 CMake toolchain file to NDK r22 directory"); + Utilities.CopyFile ( + Path.Combine (BuildPaths.XamarinAndroidSourceRoot, "src-ThirdParty", "ndk", "android.toolchain.cmake.ndk_r21.3"), + Path.Combine (context.Properties.GetRequiredValue (KnownProperties.AndroidNdkDirectory), "build", "cmake", "android.toolchain.cmake") + ); + } + // Ignore NDK property setting if not installing the NDK if (!DependencyTypeToInstall.HasFlag (AndroidToolchainComponentType.BuildDependency)) return true; else - return context.BuildInfo.GatherNDKInfo (context, ndkRoot); + return context.BuildInfo.GatherNDKInfo (context); } void CheckPackageStatus (Context context, string packageCacheDir, AndroidPackage pkg, List toDownload) diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_Binutils.cs b/build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_Binutils.cs index 1a4494d9780..b6e1095b488 100644 --- a/build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_Binutils.cs +++ b/build-tools/xaprepare/xaprepare/Steps/Step_Get_Windows_Binutils.cs @@ -79,26 +79,32 @@ public Step_Get_Windows_Binutils () protected override async Task Execute (Context context) { string ndkVersion = BuildAndroidPlatforms.AndroidNdkVersion; + string baseArchivePath = $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin"; - var neededFiles = new HashSet (StringComparer.OrdinalIgnoreCase) { - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/i686-linux-android-as.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/i686-linux-android-ld.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/i686-linux-android-strip.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/arm-linux-androideabi-as.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/arm-linux-androideabi-ld.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/arm-linux-androideabi-strip.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/x86_64-linux-android-as.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/x86_64-linux-android-ld.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/x86_64-linux-android-strip.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android-as.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android-ld.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android-strip.exe", - $"android-ndk-r{ndkVersion}/toolchains/llvm/prebuilt/windows-x86_64/bin/libwinpthread-1.dll", + var neededFiles = new Dictionary (StringComparer.OrdinalIgnoreCase) { + { $"{baseArchivePath}/libwinpthread-1.dll", String.Empty }, }; - string destinationDirectory = Path.Combine (Configurables.Paths.InstallMSBuildDir, "ndk"); + foreach (var kvp in Configurables.Defaults.AndroidToolchainPrefixes) { + string archPrefix = kvp.Value; + foreach (NDKTool ndkTool in Configurables.Defaults.NDKTools) { + string sourcePath = $"{baseArchivePath}/{archPrefix}-{ndkTool.Name}.exe"; + string destPath; + + if (ndkTool.DestinationName.Length == 0) { + destPath = String.Empty; + } else { + destPath = $"{baseArchivePath}/{archPrefix}-{ndkTool.DestinationName}.exe"; + } + + neededFiles [sourcePath] = destPath; + } + } + + string destinationDirectory = Configurables.Paths.WindowsBinutilsInstallDir; int existingFiles = 0; - foreach (string f in neededFiles) { + foreach (var kvp in neededFiles) { + string f = GetDestinationFile (kvp); string file = Path.Combine (destinationDirectory, Path.GetFileName (f)); string stampFile = GetStampFile (f, destinationDirectory, ndkVersion); if (File.Exists (file)) { @@ -112,7 +118,7 @@ protected override async Task Execute (Context context) } if (existingFiles == neededFiles.Count) { - Log.StatusLine ("All Windows GAS binaries already downloaded."); + Log.StatusLine ("All Windows binutils binaries already downloaded."); return true; } @@ -129,10 +135,16 @@ protected override async Task Execute (Context context) return true; } - void StampFiles (HashSet neededFiles, string destinationDirectory, string ndkVersion) + string GetDestinationFile (KeyValuePair kvp) + { + return kvp.Value.Length == 0 ? kvp.Key : kvp.Value; + } + + void StampFiles (Dictionary neededFiles, string destinationDirectory, string ndkVersion) { var now = DateTime.UtcNow; - foreach (string file in neededFiles) { + foreach (var kvp in neededFiles) { + string file = GetDestinationFile (kvp); File.WriteAllText (GetStampFile (file, destinationDirectory, ndkVersion), now.ToString ()); } } @@ -142,7 +154,7 @@ string GetStampFile (string file, string destinationDirectory, string ndkVersion return Path.Combine (destinationDirectory, $"{Path.GetFileName (file)}.{ndkVersion}"); } - async Task FetchFiles (HashSet neededFiles, string destinationDirectory, Uri url) + async Task FetchFiles (Dictionary neededFiles, string destinationDirectory, Uri url) { Utilities.CreateDirectory (destinationDirectory); using (var httpClient = new HttpClient ()) { @@ -185,9 +197,10 @@ async Task FetchFiles (HashSet neededFiles, string destinationDire return true; } - async Task ProcessEntries (HttpClient httpClient, Uri url, EOCD eocd, Stream centralDirectory, HashSet neededFiles, string destinationDirectory) + async Task ProcessEntries (HttpClient httpClient, Uri url, EOCD eocd, Stream centralDirectory, Dictionary neededFiles, string destinationDirectory) { long foundEntries = 0; + var foundFiles = new HashSet (StringComparer.OrdinalIgnoreCase); using (var br = new BinaryReader (centralDirectory)) { long nread = 0; @@ -201,27 +214,38 @@ async Task ProcessEntries (HttpClient httpClient, Uri url, EOCD eocd, Stre return false; } - if (!neededFiles.Contains (cdh.FileName)) + if (!neededFiles.TryGetValue (cdh.FileName, out string destinationFileName)) continue; - if (!await ReadEntry (httpClient, url, cdh, br, destinationDirectory)) + foundFiles.Add (cdh.FileName); + + if (!await ReadEntry (httpClient, url, cdh, br, destinationDirectory, destinationFileName)) return false; foundEntries++; } } if (foundEntries < neededFiles.Count) { - Log.ErrorLine ($"Could not find all required binaries. Found {foundEntries} out of {neededFiles.Count}"); + Log.ErrorLine (); + Log.ErrorLine ($"Could not find all required binaries. Found {foundEntries} out of {neededFiles.Count}, the missing files are:"); + foreach (string file in neededFiles.Keys) { + if (foundFiles.Contains (file)) { + continue; + } + Log.StatusLine ($" {Context.Instance.Characters.Bullet} {file} "); + } + return false; } return true; } - async Task ReadEntry (HttpClient httpClient, Uri url, CDHeader cdh, BinaryReader br, string destinationDirectory) + async Task ReadEntry (HttpClient httpClient, Uri url, CDHeader cdh, BinaryReader br, string destinationDirectory, string destinationFileName) { Context context = Context.Instance; - string destFilePath = Path.Combine (destinationDirectory, Path.GetFileName (cdh.FileName)); + string destFileName = Path.GetFileName (destinationFileName.Length == 0 ? cdh.FileName : destinationFileName); + string destFilePath = Path.Combine (destinationDirectory, destFileName); string compressedFilePath = Path.Combine (destinationDirectory, $"{destFilePath}.deflated"); Log.Status ($" {context.Characters.Bullet} {Path.GetFileName (cdh.FileName)} "); Log.Status ($"{context.Characters.RightArrow}", ConsoleColor.Cyan); diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_InstallNDKBinutils.cs b/build-tools/xaprepare/xaprepare/Steps/Step_InstallNDKBinutils.cs new file mode 100644 index 00000000000..e8456ffd649 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_InstallNDKBinutils.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Xamarin.Android.Prepare +{ + class Step_InstallNDKBinutils : Step + { + AndroidToolchainComponentType dependencyTypeToInstall; + + public Step_InstallNDKBinutils (AndroidToolchainComponentType dependencyTypeToInstall = AndroidToolchainComponentType.All) + : base ("Install host NDK binutils") + { + this.dependencyTypeToInstall = dependencyTypeToInstall; + } + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + // Ignore copying if not installing the NDK + if (!dependencyTypeToInstall.HasFlag (AndroidToolchainComponentType.BuildDependency)) { + Log.DebugLine ("NDK is not being installed, binutils installation skipped."); + return true; + } + + string ndkRoot = context.Properties.GetRequiredValue (KnownProperties.AndroidNdkDirectory); + + string sourceDirectory = Configurables.Paths.AndroidToolchainBinDirectory; + string destinationDirectory = Configurables.Paths.HostBinutilsInstallDir; + + Log.StatusLine ("Copying host binutils:"); + foreach (var kvp in Configurables.Defaults.AndroidToolchainPrefixes) { + string archPrefix = kvp.Value; + foreach (NDKTool ndkTool in Configurables.Defaults.NDKTools) { + string sourcePath = context.OS.AppendExecutableExtension (Path.Combine (sourceDirectory, $"{archPrefix}-{ndkTool.Name}")); + string destName = ndkTool.DestinationName.Length == 0 ? ndkTool.Name : ndkTool.DestinationName; + string destPath = context.OS.AppendExecutableExtension (Path.Combine (destinationDirectory, $"{archPrefix}-{destName}")); + + Log.Status ($" {context.Characters.Bullet} {Path.GetFileName (sourcePath)} "); + Log.Status ($"{context.Characters.RightArrow}", ConsoleColor.Cyan); + Log.StatusLine ($" {Utilities.GetRelativePath (BuildPaths.XamarinAndroidSourceRoot, destPath)}"); + Utilities.CopyFile (sourcePath, destPath); + } + } + + return true; + } +#pragma warning restore CS1998 + } +} diff --git a/build-tools/xaprepare/xaprepare/xaprepare.targets b/build-tools/xaprepare/xaprepare/xaprepare.targets index fe9752b73df..64bc52bf2aa 100644 --- a/build-tools/xaprepare/xaprepare/xaprepare.targets +++ b/build-tools/xaprepare/xaprepare/xaprepare.targets @@ -59,6 +59,7 @@ + diff --git a/src-ThirdParty/ndk/android.toolchain.cmake.ndk_r21.3 b/src-ThirdParty/ndk/android.toolchain.cmake.ndk_r21.3 new file mode 100644 index 00000000000..c3a56a96997 --- /dev/null +++ b/src-ThirdParty/ndk/android.toolchain.cmake.ndk_r21.3 @@ -0,0 +1,726 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configurable variables. +# Modeled after the ndk-build system. +# For any variables defined in: +# https://developer.android.com/ndk/guides/android_mk.html +# https://developer.android.com/ndk/guides/application_mk.html +# if it makes sense for CMake, then replace LOCAL, APP, or NDK with ANDROID, and +# we have that variable below. +# +# ANDROID_TOOLCHAIN +# ANDROID_ABI +# ANDROID_PLATFORM +# ANDROID_STL +# ANDROID_PIE +# ANDROID_CPP_FEATURES +# ANDROID_ALLOW_UNDEFINED_SYMBOLS +# ANDROID_ARM_MODE +# ANDROID_ARM_NEON +# ANDROID_DISABLE_FORMAT_STRING_CHECKS +# ANDROID_CCACHE + +cmake_minimum_required(VERSION 3.6.0) + +# Inhibit all of CMake's own NDK handling code. +set(CMAKE_SYSTEM_VERSION 1) + +# CMake invokes the toolchain file twice during the first build, but only once +# during subsequent rebuilds. This was causing the various flags to be added +# twice on the first build, and on a rebuild ninja would see only one set of the +# flags and rebuild the world. +# https://github.com/android-ndk/ndk/issues/323 +if(ANDROID_NDK_TOOLCHAIN_INCLUDED) + return() +endif(ANDROID_NDK_TOOLCHAIN_INCLUDED) +set(ANDROID_NDK_TOOLCHAIN_INCLUDED true) + +# Android NDK +get_filename_component(ANDROID_NDK_EXPECTED_PATH + "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) +if(NOT ANDROID_NDK) + set(ANDROID_NDK "${ANDROID_NDK_EXPECTED_PATH}") +else() + # Allow the user to specify their own NDK path, but emit a warning. This is an + # uncommon use case, but helpful if users want to use a bleeding edge + # toolchain file with a stable NDK. + # https://github.com/android-ndk/ndk/issues/473 + get_filename_component(ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE) + if(NOT "${ANDROID_NDK}" STREQUAL "${ANDROID_NDK_EXPECTED_PATH}") + message(WARNING "Using custom NDK path (ANDROID_NDK is set): ${ANDROID_NDK}") + endif() +endif() +unset(ANDROID_NDK_EXPECTED_PATH) +file(TO_CMAKE_PATH "${ANDROID_NDK}" ANDROID_NDK) + +# Android NDK revision +# Possible formats: +# * r16, build 1234: 16.0.1234 +# * r16b, build 1234: 16.1.1234 +# * r16 beta 1, build 1234: 16.0.1234-beta1 +# +# Canary builds are not specially marked. +file(READ "${ANDROID_NDK}/source.properties" ANDROID_NDK_SOURCE_PROPERTIES) + +set(ANDROID_NDK_REVISION_REGEX + "^Pkg\\.Desc = Android NDK\nPkg\\.Revision = ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-beta([0-9]+))?") +if(NOT ANDROID_NDK_SOURCE_PROPERTIES MATCHES "${ANDROID_NDK_REVISION_REGEX}") + message(SEND_ERROR "Failed to parse Android NDK revision: ${ANDROID_NDK}/source.properties.\n${ANDROID_NDK_SOURCE_PROPERTIES}") +endif() + +set(ANDROID_NDK_MAJOR "${CMAKE_MATCH_1}") +set(ANDROID_NDK_MINOR "${CMAKE_MATCH_2}") +set(ANDROID_NDK_BUILD "${CMAKE_MATCH_3}") +set(ANDROID_NDK_BETA "${CMAKE_MATCH_5}") +if(ANDROID_NDK_BETA STREQUAL "") + set(ANDROID_NDK_BETA "0") +endif() +set(ANDROID_NDK_REVISION + "${ANDROID_NDK_MAJOR}.${ANDROID_NDK_MINOR}.${ANDROID_NDK_BUILD}${CMAKE_MATCH_4}") + +# Touch toolchain variable to suppress "unused variable" warning. +# This happens if CMake is invoked with the same command line the second time. +if(CMAKE_TOOLCHAIN_FILE) +endif() + +# Compatibility for configurable variables. +# Compatible with configurable variables from the other toolchain file: +# https://github.com/taka-no-me/android-cmake +# TODO: We should consider dropping compatibility to simplify things once most +# of our users have migrated to our standard set of configurable variables. +if(ANDROID_TOOLCHAIN_NAME AND NOT ANDROID_TOOLCHAIN) + if(ANDROID_TOOLCHAIN_NAME MATCHES "-clang([0-9].[0-9])?$") + set(ANDROID_TOOLCHAIN clang) + elseif(ANDROID_TOOLCHAIN_NAME MATCHES "-[0-9].[0-9]$") + set(ANDROID_TOOLCHAIN gcc) + endif() +endif() +if(ANDROID_ABI STREQUAL "armeabi-v7a with NEON") + set(ANDROID_ABI armeabi-v7a) + set(ANDROID_ARM_NEON TRUE) +elseif(ANDROID_TOOLCHAIN_NAME AND NOT ANDROID_ABI) + if(ANDROID_TOOLCHAIN_NAME MATCHES "^arm-linux-androideabi-") + set(ANDROID_ABI armeabi-v7a) + elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^aarch64-linux-android-") + set(ANDROID_ABI arm64-v8a) + elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86-") + set(ANDROID_ABI x86) + elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86_64-") + set(ANDROID_ABI x86_64) + elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^mipsel-linux-android-") + set(ANDROID_ABI mips) + elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^mips64el-linux-android-") + set(ANDROID_ABI mips64) + endif() +endif() +if(ANDROID_NATIVE_API_LEVEL AND NOT ANDROID_PLATFORM) + if(ANDROID_NATIVE_API_LEVEL MATCHES "^android-[0-9]+$") + set(ANDROID_PLATFORM ${ANDROID_NATIVE_API_LEVEL}) + elseif(ANDROID_NATIVE_API_LEVEL MATCHES "^[0-9]+$") + set(ANDROID_PLATFORM android-${ANDROID_NATIVE_API_LEVEL}) + endif() +endif() +if(DEFINED ANDROID_APP_PIE AND NOT DEFINED ANDROID_PIE) + set(ANDROID_PIE "${ANDROID_APP_PIE}") +endif() +if(ANDROID_STL_FORCE_FEATURES AND NOT DEFINED ANDROID_CPP_FEATURES) + set(ANDROID_CPP_FEATURES "rtti exceptions") +endif() +if(DEFINED ANDROID_NO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS) + if(ANDROID_NO_UNDEFINED) + set(ANDROID_ALLOW_UNDEFINED_SYMBOLS FALSE) + else() + set(ANDROID_ALLOW_UNDEFINED_SYMBOLS TRUE) + endif() +endif() +if(DEFINED ANDROID_SO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS) + set(ANDROID_ALLOW_UNDEFINED_SYMBOLS "${ANDROID_SO_UNDEFINED}") +endif() +if(DEFINED ANDROID_FORCE_ARM_BUILD AND NOT ANDROID_ARM_MODE) + if(ANDROID_FORCE_ARM_BUILD) + set(ANDROID_ARM_MODE arm) + else() + set(ANDROID_ARM_MODE thumb) + endif() +endif() +if(NDK_CCACHE AND NOT ANDROID_CCACHE) + set(ANDROID_CCACHE "${NDK_CCACHE}") +endif() + +# Default values for configurable variables. +if(NOT ANDROID_TOOLCHAIN) + set(ANDROID_TOOLCHAIN clang) +elseif(ANDROID_TOOLCHAIN STREQUAL gcc) + message(FATAL_ERROR "GCC is no longer supported. See " + "https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.") +endif() +if(NOT ANDROID_ABI) + set(ANDROID_ABI armeabi-v7a) +endif() + +if(ANDROID_ABI STREQUAL armeabi) + message(FATAL_ERROR "armeabi is no longer supported. Use armeabi-v7a.") +elseif(ANDROID_ABI MATCHES "^(mips|mips64)$") + message(FATAL_ERROR "MIPS and MIPS64 are no longer supported.") +endif() + +if(ANDROID_ABI STREQUAL armeabi-v7a AND NOT DEFINED ANDROID_ARM_NEON) + set(ANDROID_ARM_NEON TRUE) +endif() + +include(${ANDROID_NDK}/build/cmake/platforms.cmake) + +# If no platform version was chosen by the user, default to the minimum version +# supported by this NDK. +if(NOT ANDROID_PLATFORM) + message(STATUS "\ +ANDROID_PLATFORM not set. Defaulting to minimum supported version +${NDK_MIN_PLATFORM_LEVEL}.") + + set(ANDROID_PLATFORM "android-${NDK_MIN_PLATFORM_LEVEL}") +endif() + +if(ANDROID_PLATFORM STREQUAL "latest") + message(STATUS + "Using latest available ANDROID_PLATFORM: ${NDK_MAX_PLATFORM_LEVEL}.") + set(ANDROID_PLATFORM "android-${NDK_MAX_PLATFORM_LEVEL}") + string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM}) +endif() + +string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM}) + +# Aliases defined by meta/platforms.json include codename aliases for platform +# API levels as well as cover any gaps in platforms that may not have had NDK +# APIs. +if(NOT "${NDK_PLATFORM_ALIAS_${ANDROID_PLATFORM_LEVEL}}" STREQUAL "") + message(STATUS "\ +${ANDROID_PLATFORM} is an alias for \ +${NDK_PLATFORM_ALIAS_${ANDROID_PLATFORM_LEVEL}}. Adjusting ANDROID_PLATFORM to \ +match.") + set(ANDROID_PLATFORM "${NDK_PLATFORM_ALIAS_${ANDROID_PLATFORM_LEVEL}}") + string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM}) +endif() + +# Pull up to the minimum supported version if an old API level was requested. +if(ANDROID_PLATFORM_LEVEL LESS NDK_MIN_PLATFORM_LEVEL) + message(STATUS "\ +${ANDROID_PLATFORM} is unsupported. Using minimum supported version \ +${NDK_MIN_PLATFORM_LEVEL}.") + set(ANDROID_PLATFORM "android-${NDK_MIN_PLATFORM_LEVEL}") + string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM}) +endif() + +# And for LP64 we need to pull up to 21. No diagnostic is provided here because +# minSdkVersion < 21 is valid for the project even though it may not be for this +# ABI. +if(ANDROID_ABI MATCHES "64(-v8a)?$" AND ANDROID_PLATFORM_LEVEL LESS 21) + set(ANDROID_PLATFORM android-21) + set(ANDROID_PLATFORM_LEVEL 21) +endif() + +# ANDROID_PLATFORM beyond the maximum is an error. The correct way to specify +# the latest version is ANDROID_PLATFORM=latest. +if(ANDROID_PLATFORM_LEVEL GREATER NDK_MAX_PLATFORM_LEVEL) + message(SEND_ERROR "\ +${ANDROID_PLATFORM} is above the maximum supported version \ +${NDK_MAX_PLATFORM_LEVEL}. Choose a supported API level or set \ +ANDROID_PLATFORM to \"latest\".") +endif() + +if(NOT ANDROID_STL) + set(ANDROID_STL c++_static) +endif() + +if("${ANDROID_STL}" STREQUAL "gnustl_shared" OR + "${ANDROID_STL}" STREQUAL "gnustl_static" OR + "${ANDROID_STL}" STREQUAL "stlport_shared" OR + "${ANDROID_STL}" STREQUAL "stlport_static") + message(FATAL_ERROR "\ +${ANDROID_STL} is no longer supported. Please switch to either c++_shared or \ +c++_static. See https://developer.android.com/ndk/guides/cpp-support.html \ +for more information.") +endif() + +set(ANDROID_PIE TRUE) +if(NOT ANDROID_ARM_MODE) + set(ANDROID_ARM_MODE thumb) +endif() + +# Export configurable variables for the try_compile() command. +set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + ANDROID_ABI + ANDROID_ALLOW_UNDEFINED_SYMBOLS + ANDROID_ARM_MODE + ANDROID_ARM_NEON + ANDROID_CCACHE + ANDROID_CPP_FEATURES + ANDROID_DISABLE_FORMAT_STRING_CHECKS + ANDROID_LD + ANDROID_PIE + ANDROID_PLATFORM + ANDROID_STL + ANDROID_TOOLCHAIN +) + +# Standard cross-compiling stuff. +set(ANDROID TRUE) +set(CMAKE_SYSTEM_NAME Android) + +# https://github.com/android-ndk/ndk/issues/890 +# +# ONLY doesn't do anything when CMAKE_FIND_ROOT_PATH is empty. Without this, +# CMake will wrongly search host sysroots for headers/libraries. The actual path +# used here is fairly meaningless since CMake doesn't handle the NDK sysroot +# layout (per-arch and per-verion subdirectories for libraries), so find_library +# is handled separately by CMAKE_SYSTEM_LIBRARY_PATH. +list(APPEND CMAKE_FIND_ROOT_PATH "${ANDROID_NDK}") + +# Allow users to override these values in case they want more strict behaviors. +# For example, they may want to prevent the NDK's libz from being picked up so +# they can use their own. +# https://github.com/android-ndk/ndk/issues/517 +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +endif() + +if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +endif() + +if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) +endif() + +# ABI. +set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI}) +if(ANDROID_ABI STREQUAL armeabi-v7a) + set(ANDROID_SYSROOT_ABI arm) + set(ANDROID_TOOLCHAIN_NAME arm-linux-androideabi) + set(CMAKE_SYSTEM_PROCESSOR armv7-a) + set(ANDROID_LLVM_TRIPLE armv7-none-linux-androideabi) +elseif(ANDROID_ABI STREQUAL arm64-v8a) + set(ANDROID_SYSROOT_ABI arm64) + set(CMAKE_SYSTEM_PROCESSOR aarch64) + set(ANDROID_TOOLCHAIN_NAME aarch64-linux-android) + set(ANDROID_LLVM_TRIPLE aarch64-none-linux-android) +elseif(ANDROID_ABI STREQUAL x86) + set(ANDROID_SYSROOT_ABI x86) + set(CMAKE_SYSTEM_PROCESSOR i686) + set(ANDROID_TOOLCHAIN_NAME i686-linux-android) + set(ANDROID_LLVM_TRIPLE i686-none-linux-android) +elseif(ANDROID_ABI STREQUAL x86_64) + set(ANDROID_SYSROOT_ABI x86_64) + set(CMAKE_SYSTEM_PROCESSOR x86_64) + set(ANDROID_TOOLCHAIN_NAME x86_64-linux-android) + set(ANDROID_LLVM_TRIPLE x86_64-none-linux-android) +else() + message(FATAL_ERROR "Invalid Android ABI: ${ANDROID_ABI}.") +endif() + +set(ANDROID_LLVM_TRIPLE "${ANDROID_LLVM_TRIPLE}${ANDROID_PLATFORM_LEVEL}") + +set(ANDROID_COMPILER_FLAGS) +set(ANDROID_COMPILER_FLAGS_CXX) +set(ANDROID_COMPILER_FLAGS_DEBUG) +set(ANDROID_COMPILER_FLAGS_RELEASE) +set(ANDROID_LINKER_FLAGS) +set(ANDROID_LINKER_FLAGS_EXE) + +if(ANDROID_LD STREQUAL lld) + list(APPEND ANDROID_LINKER_FLAGS -fuse-ld=lld) +endif() + +# Don't re-export libgcc symbols in every binary. +list(APPEND ANDROID_LINKER_FLAGS -Wl,--exclude-libs,libgcc.a) +# arm32 currently uses a linker script in place of libgcc to ensure that +# libunwind is linked in the correct order. --exclude-libs does not propagate to +# the contents of the linker script and can't be specified within the linker +# script. Hide both regardless of architecture to future-proof us in case we +# move other architectures to a linker script (which we may want to do so we +# automatically link libclangrt on other architectures). +list(APPEND ANDROID_LINKER_FLAGS -Wl,--exclude-libs,libgcc_real.a) +list(APPEND ANDROID_LINKER_FLAGS -Wl,--exclude-libs,libatomic.a) + +# STL. +set(ANDROID_CXX_STANDARD_LIBRARIES) +if(ANDROID_STL STREQUAL system) + list(APPEND ANDROID_COMPILER_FLAGS_CXX "-stdlib=libstdc++") + if(NOT "x${ANDROID_CPP_FEATURES}" STREQUAL "x") + list(APPEND ANDROID_CXX_STANDARD_LIBRARIES "-lc++abi") + if(ANDROID_PLATFORM_LEVEL LESS 21) + list(APPEND ANDROID_CXX_STANDARD_LIBRARIES "-landroid_support") + endif() + endif() +elseif(ANDROID_STL STREQUAL c++_static) + list(APPEND ANDROID_LINKER_FLAGS "-static-libstdc++") +elseif(ANDROID_STL STREQUAL c++_shared) +elseif(ANDROID_STL STREQUAL none) + list(APPEND ANDROID_COMPILER_FLAGS_CXX "-nostdinc++") + list(APPEND ANDROID_LINKER_FLAGS "-nostdlib++") +else() + message(FATAL_ERROR "Invalid Android STL: ${ANDROID_STL}.") +endif() + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) + set(ANDROID_HOST_TAG linux-x86_64) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) + set(ANDROID_HOST_TAG darwin-x86_64) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) + set(ANDROID_HOST_TAG windows-x86_64) +endif() + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) + set(ANDROID_TOOLCHAIN_SUFFIX .exe) +endif() + +# Toolchain. +set(ANDROID_TOOLCHAIN_ROOT + "${ANDROID_NDK}/toolchains/llvm/prebuilt/${ANDROID_HOST_TAG}") +set(ANDROID_TOOLCHAIN_PREFIX + "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_NAME}-") + +list(APPEND CMAKE_PREFIX_PATH "${ANDROID_TOOLCHAIN_ROOT}") + +# find_library searches a handful of paths as described by +# https://cmake.org/cmake/help/v3.6/command/find_library.html. CMake doesn't +# understand the Android sysroot layout, so we need to give the direct path to +# the libraries rather than just the sysroot. Set up CMAKE_SYSTEM_LIBRARY_PATH +# (https://cmake.org/cmake/help/v3.6/variable/CMAKE_SYSTEM_LIBRARY_PATH.html) +# instead. + +# NB: This variable causes CMake to automatically pass --sysroot to the +# toolchain. Studio currently relies on this to recognize Android builds. If +# this variable is removed, ensure that flag is still passed. +# TODO: Teach Studio to recognize Android builds based on --target. +set(CMAKE_SYSROOT "${ANDROID_TOOLCHAIN_ROOT}/sysroot") + +# Allows CMake to find headers in the architecture-specific include directories. +set(CMAKE_LIBRARY_ARCHITECTURE "${ANDROID_TOOLCHAIN_NAME}") + +# Instructs CMake to search the correct API level for libraries. +list(APPEND CMAKE_SYSTEM_LIBRARY_PATH + "/usr/lib/${ANDROID_TOOLCHAIN_NAME}/${ANDROID_PLATFORM_LEVEL}") + +set(ANDROID_HOST_PREBUILTS "${ANDROID_NDK}/prebuilt/${ANDROID_HOST_TAG}") + +set(ANDROID_C_COMPILER + "${ANDROID_TOOLCHAIN_ROOT}/bin/clang${ANDROID_TOOLCHAIN_SUFFIX}") +set(ANDROID_CXX_COMPILER + "${ANDROID_TOOLCHAIN_ROOT}/bin/clang++${ANDROID_TOOLCHAIN_SUFFIX}") +set(ANDROID_ASM_COMPILER + "${ANDROID_TOOLCHAIN_ROOT}/bin/clang${ANDROID_TOOLCHAIN_SUFFIX}") +# Clang can fail to compile if CMake doesn't correctly supply the target and +# external toolchain, but to do so, CMake needs to already know that the +# compiler is clang. Tell CMake that the compiler is really clang, but don't +# use CMakeForceCompiler, since we still want compile checks. We only want +# to skip the compiler ID detection step. +set(CMAKE_C_COMPILER_ID_RUN TRUE) +set(CMAKE_CXX_COMPILER_ID_RUN TRUE) +set(CMAKE_C_COMPILER_ID Clang) +set(CMAKE_CXX_COMPILER_ID Clang) +set(CMAKE_C_COMPILER_VERSION 9.0) +set(CMAKE_CXX_COMPILER_VERSION 9.0) +set(CMAKE_C_STANDARD_COMPUTED_DEFAULT 11) +set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT 14) +set(CMAKE_C_COMPILER_TARGET ${ANDROID_LLVM_TRIPLE}) +set(CMAKE_C_COMPILER_FRONTEND_VARIANT "GNU") +set(CMAKE_CXX_COMPILER_FRONTEND_VARIANT "GNU") +set(CMAKE_CXX_COMPILER_TARGET ${ANDROID_LLVM_TRIPLE}) +set(CMAKE_ASM_COMPILER_TARGET ${ANDROID_LLVM_TRIPLE}) +set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN "${ANDROID_TOOLCHAIN_ROOT}") +set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN "${ANDROID_TOOLCHAIN_ROOT}") +set(CMAKE_ASM_COMPILER_EXTERNAL_TOOLCHAIN "${ANDROID_TOOLCHAIN_ROOT}") +set(ANDROID_AR "${ANDROID_TOOLCHAIN_PREFIX}ar${ANDROID_TOOLCHAIN_SUFFIX}") +set(ANDROID_RANLIB + "${ANDROID_TOOLCHAIN_PREFIX}ranlib${ANDROID_TOOLCHAIN_SUFFIX}") + +# Generic flags. +list(APPEND ANDROID_COMPILER_FLAGS + -g + -DANDROID + -fdata-sections + -ffunction-sections + -funwind-tables + -fstack-protector-strong + -no-canonical-prefixes) + +# https://github.com/android/ndk/issues/885 +# If we're using LLD we need to use a slower build-id algorithm to work around +# the old version of LLDB in Android Studio, which doesn't understand LLD's +# default hash ("fast"). +# +# Note that because we cannot see the user's flags, we can't detect this very +# accurately. Users that explicitly use -fuse-ld=lld instead of ANDROID_LD will +# not be able to debug. +if(ANDROID_LD STREQUAL lld) + list(APPEND ANDROID_LINKER_FLAGS -Wl,--build-id=sha1) +else() + list(APPEND ANDROID_LINKER_FLAGS -Wl,--build-id) +endif() + +list(APPEND ANDROID_LINKER_FLAGS -Wl,--fatal-warnings) +list(APPEND ANDROID_LINKER_FLAGS_EXE -Wl,--gc-sections) + +# Debug and release flags. +list(APPEND ANDROID_COMPILER_FLAGS_DEBUG -O0) +if(ANDROID_ABI MATCHES "^armeabi" AND ANDROID_ARM_MODE STREQUAL thumb) + list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -Oz) +else() + list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -O2) +endif() +list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -DNDEBUG) +if(ANDROID_TOOLCHAIN STREQUAL clang) + list(APPEND ANDROID_COMPILER_FLAGS_DEBUG -fno-limit-debug-info) +endif() + +# Toolchain and ABI specific flags. +if(ANDROID_ABI STREQUAL x86 AND ANDROID_PLATFORM_LEVEL LESS 24) + # http://b.android.com/222239 + # http://b.android.com/220159 (internal http://b/31809417) + # x86 devices have stack alignment issues. + list(APPEND ANDROID_COMPILER_FLAGS -mstackrealign) +endif() + +list(APPEND ANDROID_COMPILER_FLAGS -D_FORTIFY_SOURCE=2) + +# STL specific flags. +if(ANDROID_STL MATCHES "^c\\+\\+_") + if(ANDROID_ABI MATCHES "^armeabi") + list(APPEND ANDROID_LINKER_FLAGS "-Wl,--exclude-libs,libunwind.a") + endif() +endif() + +set(CMAKE_C_STANDARD_LIBRARIES_INIT "-latomic -lm") +set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}") +if(ANDROID_CXX_STANDARD_LIBRARIES) + string(REPLACE ";" "\" \"" ANDROID_CXX_STANDARD_LIBRARIES "\"${ANDROID_CXX_STANDARD_LIBRARIES}\"") + set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_CXX_STANDARD_LIBRARIES_INIT} ${ANDROID_CXX_STANDARD_LIBRARIES}") +endif() + +# Configuration specific flags. + +# PIE is supported on all currently supported Android releases, but it is not +# supported with static executables, so we still provide ANDROID_PIE as an +# escape hatch for those. +if(ANDROID_PIE) + set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) +endif() + +if(ANDROID_CPP_FEATURES) + separate_arguments(ANDROID_CPP_FEATURES) + foreach(feature ${ANDROID_CPP_FEATURES}) + if(NOT ${feature} MATCHES "^(rtti|exceptions|no-rtti|no-exceptions)$") + message(FATAL_ERROR "Invalid Android C++ feature: ${feature}.") + endif() + list(APPEND ANDROID_COMPILER_FLAGS_CXX + -f${feature}) + endforeach() + string(REPLACE ";" " " ANDROID_CPP_FEATURES "${ANDROID_CPP_FEATURES}") +endif() +if(NOT ANDROID_ALLOW_UNDEFINED_SYMBOLS) + list(APPEND ANDROID_LINKER_FLAGS + -Wl,--no-undefined) +endif() +if(ANDROID_ABI MATCHES "armeabi") + # Clang does not set this up properly when using -fno-integrated-as. + # https://github.com/android-ndk/ndk/issues/906 + list(APPEND ANDROID_COMPILER_FLAGS "-march=armv7-a") + if(ANDROID_ARM_MODE STREQUAL thumb) + list(APPEND ANDROID_COMPILER_FLAGS -mthumb) + elseif(ANDROID_ARM_MODE STREQUAL arm) + # Default behavior. + else() + message(FATAL_ERROR "Invalid Android ARM mode: ${ANDROID_ARM_MODE}.") + endif() + if(ANDROID_ABI STREQUAL armeabi-v7a AND NOT ANDROID_ARM_NEON) + list(APPEND ANDROID_COMPILER_FLAGS + -mfpu=vfpv3-d16) + endif() +endif() + +# CMake automatically forwards all compiler flags to the linker, and clang +# doesn't like having -Wa flags being used for linking. To prevent CMake from +# doing this would require meddling with the CMAKE__COMPILE_OBJECT rules, +# which would get quite messy. +list(APPEND ANDROID_LINKER_FLAGS -Qunused-arguments) + +if(ANDROID_DISABLE_FORMAT_STRING_CHECKS) + list(APPEND ANDROID_COMPILER_FLAGS + -Wno-error=format-security) +else() + list(APPEND ANDROID_COMPILER_FLAGS + -Wformat -Werror=format-security) +endif() + +# Convert these lists into strings. +string(REPLACE ";" " " ANDROID_COMPILER_FLAGS "${ANDROID_COMPILER_FLAGS}") +string(REPLACE ";" " " ANDROID_COMPILER_FLAGS_CXX "${ANDROID_COMPILER_FLAGS_CXX}") +string(REPLACE ";" " " ANDROID_COMPILER_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG}") +string(REPLACE ";" " " ANDROID_COMPILER_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE}") +string(REPLACE ";" " " ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}") +string(REPLACE ";" " " ANDROID_LINKER_FLAGS_EXE "${ANDROID_LINKER_FLAGS_EXE}") + +if(ANDROID_CCACHE) + set(CMAKE_C_COMPILER_LAUNCHER "${ANDROID_CCACHE}") + set(CMAKE_CXX_COMPILER_LAUNCHER "${ANDROID_CCACHE}") +endif() +set(CMAKE_C_COMPILER "${ANDROID_C_COMPILER}") +set(CMAKE_CXX_COMPILER "${ANDROID_CXX_COMPILER}") +set(CMAKE_AR "${ANDROID_AR}" CACHE FILEPATH "Archiver") +set(CMAKE_RANLIB "${ANDROID_RANLIB}" CACHE FILEPATH "Ranlib") +set(_CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_PREFIX}") + +if(ANDROID_ABI STREQUAL "x86" OR ANDROID_ABI STREQUAL "x86_64") + set(CMAKE_ASM_NASM_COMPILER + "${ANDROID_TOOLCHAIN_ROOT}/bin/yasm${ANDROID_TOOLCHAIN_SUFFIX}") + set(CMAKE_ASM_NASM_COMPILER_ARG1 "-DELF") +endif() + +# Set or retrieve the cached flags. +# This is necessary in case the user sets/changes flags in subsequent +# configures. If we included the Android flags in here, they would get +# overwritten. +set(CMAKE_C_FLAGS "" + CACHE STRING "Flags used by the compiler during all build types.") +set(CMAKE_CXX_FLAGS "" + CACHE STRING "Flags used by the compiler during all build types.") +set(CMAKE_ASM_FLAGS "" + CACHE STRING "Flags used by the compiler during all build types.") +set(CMAKE_C_FLAGS_DEBUG "" + CACHE STRING "Flags used by the compiler during debug builds.") +set(CMAKE_CXX_FLAGS_DEBUG "" + CACHE STRING "Flags used by the compiler during debug builds.") +set(CMAKE_ASM_FLAGS_DEBUG "" + CACHE STRING "Flags used by the compiler during debug builds.") +set(CMAKE_C_FLAGS_RELEASE "" + CACHE STRING "Flags used by the compiler during release builds.") +set(CMAKE_CXX_FLAGS_RELEASE "" + CACHE STRING "Flags used by the compiler during release builds.") +set(CMAKE_ASM_FLAGS_RELEASE "" + CACHE STRING "Flags used by the compiler during release builds.") +set(CMAKE_MODULE_LINKER_FLAGS "" + CACHE STRING "Flags used by the linker during the creation of modules.") +set(CMAKE_SHARED_LINKER_FLAGS "" + CACHE STRING "Flags used by the linker during the creation of dll's.") +set(CMAKE_EXE_LINKER_FLAGS "" + CACHE STRING "Flags used by the linker.") + +set(CMAKE_C_FLAGS "${ANDROID_COMPILER_FLAGS} ${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${ANDROID_COMPILER_FLAGS} ${ANDROID_COMPILER_FLAGS_CXX} ${CMAKE_CXX_FLAGS}") +set(CMAKE_ASM_FLAGS "${ANDROID_COMPILER_FLAGS} ${CMAKE_ASM_FLAGS}") +set(CMAKE_C_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}") +set(CMAKE_CXX_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}") +set(CMAKE_ASM_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG} ${CMAKE_ASM_FLAGS_DEBUG}") +set(CMAKE_C_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}") +set(CMAKE_ASM_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE} ${CMAKE_ASM_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}") +set(CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}") +set(CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${ANDROID_LINKER_FLAGS_EXE} ${CMAKE_EXE_LINKER_FLAGS}") + +# Compatibility for read-only variables. +# Read-only variables for compatibility with the other toolchain file. +# We'll keep these around for the existing projects that still use them. +# TODO: All of the variables here have equivalents in our standard set of +# configurable variables, so we can remove these once most of our users migrate +# to those variables. +set(ANDROID_NATIVE_API_LEVEL ${ANDROID_PLATFORM_LEVEL}) +if(ANDROID_ALLOW_UNDEFINED_SYMBOLS) + set(ANDROID_SO_UNDEFINED TRUE) +else() + set(ANDROID_NO_UNDEFINED TRUE) +endif() +set(ANDROID_FUNCTION_LEVEL_LINKING TRUE) +set(ANDROID_GOLD_LINKER TRUE) +set(ANDROID_NOEXECSTACK TRUE) +set(ANDROID_RELRO TRUE) +if(ANDROID_ARM_MODE STREQUAL arm) + set(ANDROID_FORCE_ARM_BUILD TRUE) +endif() +if(ANDROID_CPP_FEATURES MATCHES "rtti" + AND ANDROID_CPP_FEATURES MATCHES "exceptions") + set(ANDROID_STL_FORCE_FEATURES TRUE) +endif() +if(ANDROID_CCACHE) + set(NDK_CCACHE "${ANDROID_CCACHE}") +endif() +if(ANDROID_TOOLCHAIN STREQUAL clang) + set(ANDROID_TOOLCHAIN_NAME ${ANDROID_TOOLCHAIN_NAME}-clang) +else() + set(ANDROID_TOOLCHAIN_NAME ${ANDROID_TOOLCHAIN_NAME}-4.9) +endif() +set(ANDROID_NDK_HOST_X64 TRUE) +set(ANDROID_NDK_LAYOUT RELEASE) +if(ANDROID_ABI STREQUAL armeabi-v7a) + set(ARMEABI_V7A TRUE) + if(ANDROID_ARM_NEON) + set(NEON TRUE) + endif() +elseif(ANDROID_ABI STREQUAL arm64-v8a) + set(ARM64_V8A TRUE) +elseif(ANDROID_ABI STREQUAL x86) + set(X86 TRUE) +elseif(ANDROID_ABI STREQUAL x86_64) + set(X86_64 TRUE) +endif() +set(ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_HOST_TAG}) +set(ANDROID_NDK_ABI_NAME ${ANDROID_ABI}) +set(ANDROID_NDK_RELEASE r${ANDROID_NDK_REVISION}) +set(ANDROID_ARCH_NAME ${ANDROID_SYSROOT_ABI}) +set(TOOL_OS_SUFFIX ${ANDROID_TOOLCHAIN_SUFFIX}) +if(ANDROID_TOOLCHAIN STREQUAL clang) + set(ANDROID_COMPILER_IS_CLANG TRUE) +endif() + +# CMake 3.7+ compatibility. +if (CMAKE_VERSION VERSION_GREATER 3.7.0) + set(CMAKE_ANDROID_NDK ${ANDROID_NDK}) + set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang) + + set(CMAKE_ANDROID_STL_TYPE ${ANDROID_STL}) + + if(ANDROID_ABI MATCHES "^armeabi(-v7a)?$") + set(CMAKE_ANDROID_ARM_NEON ${ANDROID_ARM_NEON}) + set(CMAKE_ANDROID_ARM_MODE ${ANDROID_ARM_MODE}) + endif() + + # https://github.com/android/ndk/issues/861 + if(ANDROID_ABI STREQUAL armeabi-v7a) + set(CMAKE_ANDROID_ARCH arm) + elseif(ANDROID_ABI STREQUAL arm64-v8a) + set(CMAKE_ANDROID_ARCH arm64) + elseif(ANDROID_ABI STREQUAL x86) + set(CMAKE_ANDROID_ARCH x86) + elseif(ANDROID_ABI STREQUAL x86_64) + set(CMAKE_ANDROID_ARCH x86_64) + endif() + + # https://github.com/android/ndk/issues/1012 + set(CMAKE_ASM_ANDROID_TOOLCHAIN_MACHINE "${ANDROID_TOOLCHAIN_NAME}") + set(CMAKE_C_ANDROID_TOOLCHAIN_MACHINE "${ANDROID_TOOLCHAIN_NAME}") + set(CMAKE_CXX_ANDROID_TOOLCHAIN_MACHINE "${ANDROID_TOOLCHAIN_NAME}") + + set(CMAKE_ASM_ANDROID_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_PREFIX}") + set(CMAKE_C_ANDROID_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_PREFIX}") + set(CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_PREFIX}") + + set(CMAKE_ASM_ANDROID_TOOLCHAIN_SUFFIX "${ANDROID_TOOLCHAIN_SUFFIX}") + set(CMAKE_C_ANDROID_TOOLCHAIN_SUFFIX "${ANDROID_TOOLCHAIN_SUFFIX}") + set(CMAKE_CXX_ANDROID_TOOLCHAIN_SUFFIX "${ANDROID_TOOLCHAIN_SUFFIX}") +endif() diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs index b15e28eb5eb..97d7388e193 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs @@ -86,7 +86,8 @@ public class Aot : AndroidAsyncTask public override bool RunTask () { - if (EnableLLVM && !NdkUtil.Init (LogCodedError, AndroidNdkDirectory)) + // NdkUtil must always be initialized - once per thread + if (!NdkUtil.Init (LogCodedError, AndroidNdkDirectory)) return false; return base.RunTask (); @@ -229,6 +230,12 @@ int GetNdkApiLevel(string androidNdkPath, string androidApiLevel, AndroidTargetA public async override System.Threading.Tasks.Task RunTaskAsync () { + // NdkUtil must always be initialized - once per thread + if (!NdkUtil.Init (LogCodedError, AndroidNdkDirectory)) { + LogDebugMessage ("Failed to initialize NdkUtil"); + return; + } + bool hasValidAotMode = GetAndroidAotMode (AndroidAotMode, out AotMode); if (!hasValidAotMode) { LogCodedError ("XA3002", Properties.Resources.XA3002, AndroidAotMode); @@ -376,6 +383,19 @@ IEnumerable GetAotConfigs () ldFlags = string.Join(";", libs); } + string ldName = String.Empty; + if (EnableLLVM) { + ldName = NdkUtil.GetNdkTool (AndroidNdkDirectory, arch, "ld", level); + if (!String.IsNullOrEmpty (ldName)) { + ldName = Path.GetFileName (ldName); + if (ldName.IndexOf ('-') >= 0) { + ldName = ldName.Substring (ldName.LastIndexOf ("-") + 1); + } + } + } else { + ldName = "ld"; + } + foreach (var assembly in ResolvedAssemblies) { string outputFile = Path.Combine(outdir, string.Format ("libaot-{0}.so", Path.GetFileName (assembly.ItemSpec))); @@ -409,6 +429,12 @@ IEnumerable GetAotConfigs () aotOptions.Add ($"tool-prefix={toolPrefix}"); aotOptions.Add ($"llvm-path={sdkBinDirectory}"); aotOptions.Add ($"temp-path={tempDir}"); + + if (!String.IsNullOrEmpty (ldName)) { + // MUST be before `ld-flags`, otherwise Mono fails to parse it on Windows + aotOptions.Add ($"ld-name={ldName}"); + } + aotOptions.Add ($"ld-flags={ldFlags}"); // we need to quote the entire --aot arguments here to make sure it is parsed diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/NdkUtils.cs b/src/Xamarin.Android.Build.Tasks/Tasks/NdkUtils.cs index cb474972230..851e4fb83ad 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/NdkUtils.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/NdkUtils.cs @@ -36,7 +36,6 @@ public static bool Init (Action logError, string ndkPath) { Version ndkVersion; bool hasNdkVersion = GetNdkToolchainRelease (ndkPath ?? "", out ndkVersion); - if (!hasNdkVersion) { logError ("XA5104", Properties.Resources.XA5104); return false; @@ -210,21 +209,48 @@ public static string GetNdkTool (string androidNdkPath, AndroidTargetArch arch, if (forCompiler && OS.IsWindows) toolName = $"{toolName}{extension}"; else - toolName = $"{toolchainPrefix}-{toolName}{extension}"; + toolName = GetPrefixedName (toolName); + + string toolPath = GetToolPath (toolName); + if (String.IsNullOrEmpty (toolPath) && String.Compare ("ld", tool, StringComparison.OrdinalIgnoreCase) == 0) { + // NDK r22 removed arch-prefixed `ld` binary. There exists the unprefixed `ld` binary, from the LLVM + // toolchain, and two binutils linkers - `ld.bfd` and `ld.gold`. Since we will need to keep using + // binutils once NDK removes them, let's use one of the latter. `ld.gold` is the better choice, so we'll + // use it if found + toolPath = GetToolPath (GetPrefixedName ("ld.gold")); + } - string binDir = Path.Combine (toolchainDir, "bin"); - string toolExe = MonoAndroidHelper.GetExecutablePath (binDir, toolName); - string toolPath = Path.Combine (binDir, toolExe); - if (File.Exists (toolPath)) + if (!String.IsNullOrEmpty (toolPath)) { return toolPath; + } Diagnostic.Error (5105, Properties.Resources.XA5105, toolName, arch, toolchainDir); return null; + + string GetPrefixedName (string name) + { + return $"{toolchainPrefix}-{name}{extension}"; + } + + string GetToolPath (string name) + { + string binDir = Path.Combine (toolchainDir, "bin"); + string toolExe = MonoAndroidHelper.GetExecutablePath (binDir, name); + string toolPath = Path.Combine (binDir, toolExe); + if (File.Exists (toolPath)) + return toolPath; + return null; + } } static string GetUnifiedHeadersPath (string androidNdkPath) { - return Path.Combine (androidNdkPath, "sysroot", "usr", "include"); + string preNdk22SysrootIncludeDir = Path.Combine (androidNdkPath, "sysroot", "usr", "include"); + if (Directory.Exists (preNdk22SysrootIncludeDir)) { + return preNdk22SysrootIncludeDir; + } + + return Path.Combine (GetToolchainDir (androidNdkPath), "sysroot", "usr", "include"); } public static string GetArchDirName (AndroidTargetArch arch) @@ -275,10 +301,19 @@ public static string GetNdkPlatformLibPath (string androidNdkPath, AndroidTarget if (!UsingClangNDK) return NdkUtilOld.GetNdkPlatformLibPath (androidNdkPath, arch, apiLevel); + var checkedPaths = new List (); string lib = arch == AndroidTargetArch.X86_64 ? "lib64" : "lib"; string path = Path.Combine (androidNdkPath, "platforms", $"android-{apiLevel}", $"arch-{GetPlatformArch (arch)}", "usr", lib); - if (!Directory.Exists (path)) - throw new InvalidOperationException ($"Platform library directory for target {arch} and API Level {apiLevel} was not found. Expected path is \"{path}\""); + if (!Directory.Exists (path)) { + checkedPaths.Add (path); + path = Path.Combine (GetNdk22OrNewerSysrootDir (androidNdkPath), GetArchDirName (arch), apiLevel.ToString ()); + } + + if (!Directory.Exists (path)) { + checkedPaths.Add (path); + string paths = String.Join ("; ", checkedPaths); + throw new InvalidOperationException ($"Platform library directory for target {arch} and API Level {apiLevel} was not found. Checked paths: {paths}"); + } return path; } @@ -356,9 +391,65 @@ public static bool IsNdk64BitArch (AndroidTargetArch arch) return arch == AndroidTargetArch.Arm64 || arch == AndroidTargetArch.X86_64; } - public static IEnumerable GetSupportedPlatforms (string androidNdkPath) + static string GetNdk22OrNewerSysrootDir (string androidNdkPath) + { + return Path.Combine (GetToolchainDir (androidNdkPath), "sysroot", "usr", "lib"); + } + + public static IEnumerable GetSupportedPlatforms (TaskLoggingHelper log, string androidNdkPath) + { + string preNdk22PlatformsDir = Path.Combine (androidNdkPath, "platforms"); + + if (Directory.Exists (preNdk22PlatformsDir)) { + return GetSupportedPlatformsPreNdk22 (preNdk22PlatformsDir); + } + + // NDK r22 no longer has a single platforms dir. The API level directories are now found in per-arch + // subdirectories under the toolchain directory. We need to examine all of them and compose a list of unique + // API levels (since they are repeated in each per-arch subdirectory, but not all architectures have the + // same set of API levels) + var apiLevels = new HashSet (); + string sysrootLibDir = GetNdk22OrNewerSysrootDir (androidNdkPath); + foreach (AndroidTargetArch targetArch in Enum.GetValues (typeof (AndroidTargetArch))) { + if (targetArch == AndroidTargetArch.None || + targetArch == AndroidTargetArch.Other || + targetArch == AndroidTargetArch.Mips) { + continue; + } + + string archDirName = GetArchDirName (targetArch); + if (String.IsNullOrEmpty (archDirName)) { + log.LogWarning ($"NDK architecture {targetArch} unknown?"); + continue; + } + + string archDir = Path.Combine (sysrootLibDir, archDirName); + if (!Directory.Exists (archDir)) { + log.LogWarning ($"Architecture {targetArch} toolchain directory '{archDir}' not found"); + continue; + } + + foreach (string platform in Directory.EnumerateDirectories (archDir, "*", SearchOption.TopDirectoryOnly)) { + string plibc = Path.Combine (platform, "libc.so"); + if (!File.Exists (plibc)) { + continue; + } + + string pdir = Path.GetFileName (platform); + int api; + if (!Int32.TryParse (pdir, out api) || apiLevels.Contains (api)) { + continue; + } + apiLevels.Add (api); + } + } + + return apiLevels; + } + + static IEnumerable GetSupportedPlatformsPreNdk22 (string platformsDir) { - foreach (var platform in Directory.EnumerateDirectories (Path.Combine (androidNdkPath, "platforms"))) { + foreach (var platform in Directory.EnumerateDirectories (platformsDir)) { var androidApi = Path.GetFileName (platform); int api = -1; if (int.TryParse (androidApi.Replace ("android-", String.Empty), out api)) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs index 8e2ae279245..9332a5e31e5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs @@ -143,10 +143,20 @@ public void BuildAotApplicationAndÜmläüts (string supportedAbis, bool enableL return; //NOTE: Windows has shortened paths such as: C:\Users\myuser\ANDROI~3\ndk\PLATFO~1\AN3971~1\arch-x86\usr\lib\libc.so if (checkMinLlvmPath && !IsWindows) { + bool ndk22OrNewer = false; + if (Xamarin.Android.Tasks.NdkUtil.GetNdkToolchainRelease (AndroidNdkPath, out Xamarin.Android.Tasks.NdkUtilOld.NdkVersion ndkVersion)) { + ndk22OrNewer = ndkVersion.Version >= 22; + } + // LLVM passes a direct path to libc.so, and we need to use the libc.so // which corresponds to the *minimum* SDK version specified in AndroidManifest.xml // Since we overrode minSdkVersion=16, that means we should use libc.so from android-16. - StringAssertEx.ContainsRegex (@"\s*\[aot-compiler stdout].*android-16.arch-.*.usr.lib.libc\.so", b.LastBuildOutput, "AOT+LLVM should use libc.so from minSdkVersion!"); + if (ndk22OrNewer) { + // NDK r22 or newer store libc in [toolchain]/sysroot/usr/lib/[ARCH]/[API]/libc.so + StringAssertEx.ContainsRegex (@"\s*\[aot-compiler stdout].*sysroot.*.usr.lib.*16.libc\.so", b.LastBuildOutput, "AOT+LLVM should use libc.so from minSdkVersion!"); + } else { + StringAssertEx.ContainsRegex (@"\s*\[aot-compiler stdout].*android-16.arch-.*.usr.lib.libc\.so", b.LastBuildOutput, "AOT+LLVM should use libc.so from minSdkVersion!"); + } } foreach (var abi in supportedAbis.Split (new char [] { ';' })) { var libapp = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/NdkUtilTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/NdkUtilTests.cs index 20cc301c76e..cf0fd615c80 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/NdkUtilTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/NdkUtilTests.cs @@ -35,7 +35,7 @@ public void TestNdkUtil () var sdkDir = AndroidSdkPath; MonoAndroidHelper.AndroidSdk = new AndroidSdkInfo ((arg1, arg2) => { }, sdkDir, ndkDir); NdkUtil.Init (log, ndkDir); - var platforms = NdkUtil.GetSupportedPlatforms (ndkDir); + var platforms = NdkUtil.GetSupportedPlatforms (log, ndkDir); Assert.AreNotEqual (0, platforms.Count (), "No platforms found"); var arch = AndroidTargetArch.X86; Assert.IsTrue (NdkUtil.ValidateNdkPlatform (log, ndkDir, arch, enableLLVM: false)); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets index 2a300fd3cb9..441528321d8 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets @@ -16,7 +16,6 @@ _GenerateSupportedPlatforms; - _CopyNDKTools; _GenerateXACommonProps; _AssignRelativeParentLinkMetadata; $(BuildDependsOn); @@ -193,67 +192,6 @@ <_SharedRuntimeAssemblies Include="$(_SharedRuntimeBuildPath)$(AndroidFrameworkVersion)\OpenTK.dll" /> <_SharedRuntimeAssemblies Include="$(_SharedRuntimeBuildPath)$(AndroidFrameworkVersion)\OpenTK-1.0.dll" /> - - - <_PrebuiltToolchainDir Condition=" '$(HostOS)' == 'Linux' ">linux-x86_64 - <_PrebuiltToolchainDir Condition=" '$(HostOS)' == 'Darwin' ">darwin-x86_64 - <_PrebuiltToolchainDir Condition=" '$(HostOS)' == 'Windows' And '$(HostBits)' == '64' ">windows-x86_64 - <_PrebuiltToolchainDir Condition=" '$(HostOS)' == 'Windows' And '$(HostBits)' == '32' ">windows - - - <_ToolchainPrefix Include="aarch64-linux-android"> - aarch64-linux-android - - <_ToolchainPrefix Include="arm-linux-androideabi"> - arm-linux-androideabi - - <_ToolchainPrefix Include="x86"> - i686-linux-android - - <_ToolchainPrefix Include="x86_64"> - x86_64-linux-android - - <_ToolName Include="as"/> - <_ToolName Include="ld"/> - <_ToolName Include="strip"/> - - - - - - - - - - - - <_NDKToolFileName>$([System.IO.Path]::GetFileName ('$(_NDKToolLocation)')) - <_NDKToolDestinationBase Condition=" '$(HostOS)' != 'Windows' ">$(XAInstallPrefix)xbuild\Xamarin\Android\$(HostOS)\ndk\ - <_NDKToolDestinationBase Condition=" '$(HostOS)' == 'Windows' ">$(XAInstallPrefix)xbuild\Xamarin\Android\ndk\ - - - - <_NDKToolSource Include="$(_NDKToolLocation%(_ToolchainTool.Identity)-%(_ToolchainTool.Tool))" /> - <_NDKToolSource Condition=" '$(HostOS)' == 'Windows' " Include="$([System.IO.Path]::GetDirectoryName ('$(_NDKToolLocationaarch64-linux-android-as)'))\libwinpthread-1.dll" /> - <_NDKToolDestination Include="$(_NDKToolDestinationBase)$([System.IO.Path]::GetFileName ('$(_NDKToolLocation%(_ToolchainTool.Identity)-%(_ToolchainTool.Tool))'))" /> - <_NDKToolDestination Condition=" '$(HostOS)' == 'Windows' " Include="$(_NDKToolDestinationBase)libwinpthread-1.dll" /> - - - - - - $(CmakePath) - --debug-output $(_CmakeAndroidFlags) -DENABLE_CLANG_UBSAN=ON -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) $(MSBuildThisFileDirectory) + --debug-output $(_CmakeAndroidFlags) -DANDROID_STL="c++_static" -DANDROID_CPP_FEATURES="rtti exceptions" -DENABLE_CLANG_UBSAN=ON -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) $(MSBuildThisFileDirectory) $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.Identity)-ubsan-Debug @@ -90,7 +90,7 @@ <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - --debug-output $(_CmakeAndroidFlags) -DENABLE_CLANG_UBSAN=ON -DSTRIP_DEBUG=ON -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) $(MSBuildThisFileDirectory) + --debug-output $(_CmakeAndroidFlags) -DANDROID_STL="c++_static" -DANDROID_CPP_FEATURES="rtti exceptions" -DENABLE_CLANG_UBSAN=ON -DSTRIP_DEBUG=ON -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevel) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$(OutputPath)%(AndroidSupportedTargetJitAbi.Identity) $(MSBuildThisFileDirectory) $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.Identity)-ubsan-Release diff --git a/src/sqlite-xamarin/CMakeLists.txt b/src/sqlite-xamarin/CMakeLists.txt index a8b448692c2..894e5ade79f 100644 --- a/src/sqlite-xamarin/CMakeLists.txt +++ b/src/sqlite-xamarin/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6.0) +cmake_minimum_required(VERSION 3.10.2) # # MUST be included before project()!