From b3d2503a06ff99104f91917077f9e2132118b27e Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 6 Jul 2023 04:22:37 -0700 Subject: [PATCH] [Android] Add NdkToolFinder task & Fix up AOT app build (#88210) Since we bumped to NDK 23, having the aot compiler itself generate shared libraries stopped working. This is due to NDK 23 moving most of the toolchain into a common bin directory. AS was left over in each of these directories as an alias to bin/-as. This change adds a task to collect all of the important NDK toolchain paths. It also fixes up the aot build when AOTWithLibraryFiles is set to true and we want the aot compiler to produce shared libraries. --- Directory.Build.props | 2 + src/mono/mono/mini/aot-compiler.c | 9 +- .../msbuild/android/build/AndroidBuild.props | 4 + .../android/build/AndroidBuild.targets | 40 +++++- .../sample/Android/AndroidSampleApp.csproj | 78 ++++++----- src/mono/sample/Android/Makefile | 1 - src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 16 ++- src/tasks/Common/Utils.cs | 9 ++ .../Android/Ndk/AndroidArch.cs | 16 +++ src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs | 127 ++++++++++++++++++ .../MobileBuildTasks/Android/Ndk/NdkTools.cs | 117 ++++++++++++++++ .../Android/Ndk/NdkVersion.cs | 48 +++++++ .../Android/Tasks/NdkToolFinder.cs | 87 ++++++++++++ .../MobileBuildTasks/MobileBuildTasks.csproj | 35 +++++ ...droid.Device_Emulator.Aot_Llvm.Test.csproj | 1 + 15 files changed, 553 insertions(+), 37 deletions(-) create mode 100644 src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs create mode 100644 src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs create mode 100644 src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs create mode 100644 src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs create mode 100644 src/tasks/MobileBuildTasks/Android/Tasks/NdkToolFinder.cs create mode 100644 src/tasks/MobileBuildTasks/MobileBuildTasks.csproj diff --git a/Directory.Build.props b/Directory.Build.props index 35fc44dec29..168399e791f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -149,6 +149,7 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'AppleAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'AndroidAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MobileBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WorkloadBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)')) @@ -162,6 +163,7 @@ $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'Crossgen2Tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'Microsoft.NET.CrossGen.targets')) $([MSBuild]::NormalizePath('$(AppleAppBuilderDir)', 'AppleAppBuilder.dll')) $([MSBuild]::NormalizePath('$(AndroidAppBuilderDir)', 'AndroidAppBuilder.dll')) + $([MSBuild]::NormalizePath('$(MobileBuildTasksDir)', 'MobileBuildTasks.dll')) $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppHost', 'wasm', '$(Configuration)')) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index faa6be18c08..7873a3cf369 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -239,6 +239,7 @@ typedef struct MonoAotOptions { gboolean deterministic; gboolean allow_errors; char *tool_prefix; + char *as_prefix; char *ld_flags; char *ld_name; char *mtriple; @@ -8825,6 +8826,8 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) opts->nunbox_arbitrary_trampolines = atoi (arg + strlen ("nunbox-arbitrary-trampolines=")); } else if (str_begins_with (arg, "tool-prefix=")) { opts->tool_prefix = g_strdup (arg + strlen ("tool-prefix=")); + } else if (str_begins_with (arg, "as-prefix=")) { + opts->as_prefix = g_strdup (arg + strlen ("as-prefix=")); } else if (str_begins_with (arg, "ld-flags=")) { opts->ld_flags = g_strdup (arg + strlen ("ld-flags=")); } else if (str_begins_with (arg, "ld-name=")) { @@ -13123,6 +13126,7 @@ compile_asm (MonoAotCompile *acfg) char *command, *objfile; char *outfile_name, *tmp_outfile_name, *llvm_ofile; const char *tool_prefix = acfg->aot_opts.tool_prefix ? acfg->aot_opts.tool_prefix : ""; + const char *as_prefix = acfg->aot_opts.as_prefix ? acfg->aot_opts.as_prefix : tool_prefix; char *ld_flags = acfg->aot_opts.ld_flags ? acfg->aot_opts.ld_flags : g_strdup(""); #ifdef TARGET_WIN32_MSVC @@ -13214,7 +13218,7 @@ compile_asm (MonoAotCompile *acfg) g_string_append (acfg->as_args, "-c -x assembler "); #endif - command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS, + command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", as_prefix, AS_NAME, AS_OPTIONS, acfg->as_args ? acfg->as_args->str : "", wrap_path (objfile), wrap_path (acfg->tmpfname)); aot_printf (acfg, "Executing the native assembler: %s\n", command); @@ -13225,7 +13229,7 @@ compile_asm (MonoAotCompile *acfg) } if (acfg->llvm && !acfg->llvm_owriter) { - command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS, + command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", as_prefix, AS_NAME, AS_OPTIONS, acfg->as_args ? acfg->as_args->str : "", wrap_path (acfg->llvm_ofile), wrap_path (acfg->llvm_sfile)); aot_printf (acfg, "Executing the native assembler: %s\n", command); @@ -14222,6 +14226,7 @@ aot_opts_free (MonoAotOptions *aot_opts) g_list_free (aot_opts->direct_pinvoke_lists); g_free (aot_opts->dedup_include); g_free (aot_opts->tool_prefix); + g_free (aot_opts->as_prefix); g_free (aot_opts->ld_flags); g_free (aot_opts->ld_name); g_free (aot_opts->mtriple); diff --git a/src/mono/msbuild/android/build/AndroidBuild.props b/src/mono/msbuild/android/build/AndroidBuild.props index 64fdebc6cef..15c82b729c1 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.props +++ b/src/mono/msbuild/android/build/AndroidBuild.props @@ -4,6 +4,10 @@ true true + <_HostOS Condition="$([MSBuild]::IsOSPlatform('Windows'))">windows + <_HostOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx + <_HostOS Condition="'$(_HostOS)' == ''">linux + <_IsLibraryMode Condition="'$(NativeLib)' != ''">true Publish diff --git a/src/mono/msbuild/android/build/AndroidBuild.targets b/src/mono/msbuild/android/build/AndroidBuild.targets index 26cc272768a..7d6f1504812 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.targets +++ b/src/mono/msbuild/android/build/AndroidBuild.targets @@ -8,6 +8,9 @@ false + @@ -37,7 +40,6 @@ $(AndroidBundleDir) <_MonoHeaderPath>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0')) - <_AotModuleTablePath>$(AndroidBundleDir)\modules.c @@ -94,6 +96,15 @@ <_AOTMode Condition="'$(UseMonoJustInterp)' != 'true'">Normal <_AOTMode Condition="'$(UseMonoJustInterp)' == 'true'">JustInterp <_AOTMode Condition="'$(ForceFullAOT)' == 'true'">Full + <_AotOutputType>AsmOnly + + + + <_AotOutputType>Library + <_AotLibraryFormat>So + + + <_AotModuleTablePath>$(AndroidBundleDir)\modules.c @@ -126,6 +137,28 @@ @(MonoAOTCompilerDefaultProcessArguments, ';') + + 21 + + + + + + + + + + + + + <_AsPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_AsPrefixPath)')) + <_ToolPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_ToolPrefixPath)')) + + <_AotInputAssemblies Include="@(_AssembliesToBundleInternal)" Condition="'%(_AssembliesToBundleInternal._InternalForceInterpret)' != 'true'"> @@ -182,17 +215,20 @@ diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index 246100b188e..90dbe89a10e 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -11,8 +11,13 @@ - - + + + <_MobileIntermediateOutputPath>$(IntermediateOutputPath)mobile @@ -36,46 +41,57 @@ Condition="'$(ForceAOT)' == 'true'"/> - - arm64-v8a - armeabi-v7a - x86_64 - x86 - - - - <_AotOutputType>AsmOnly - <_AotModulesTablePath>$(ApkDir)\modules.c - - <_AotOutputType>Library <_AotLibraryFormat>So - <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux-x86_64 - <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">darwin-x86_64 - <_PrebuiltOS Condition="$([MSBuild]::IsOSPlatform('Windows'))">windows-x86_64 - <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'arm'">arm-linux-androideabi$ - <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'arm64'">aarch64-linux-android - <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'x64'">x86_64-linux-android - <_PrebuiltAbi Condition="'$(TargetArchitecture)' == 'x86'">i686-linux-android - <_AotToolPrefix>$(ANDROID_NDK_ROOT)\toolchains\llvm\prebuilt\$(_PrebuiltOS)\bin\$(_PrebuiltAbi)- + <_AotMode Condition="'$(AotMode)' == ''">Normal + + + + <_AotOutputType>AsmOnly + <_AotModulesTablePath>$(ApkDir)\modules.c + <_AotMode Condition="'$(AotMode)' == ''">Full + + + + 21 + + + + + + + + + + + + + <_AsPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_AsPrefixPath)')) + <_ToolPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_ToolPrefixPath)')) + LibraryFormat="$(_AotLibraryFormat)" + LLVMPath="$(MonoAotCrossDir)" + Mode="$(_AotMode)" + OutputDir="$(_MobileIntermediateOutputPath)" + OutputType="$(_AotOutputType)" + ToolPrefix="$(_ToolPrefixPath)" + UseAotDataFile="false" + UseLLVM="$(UseLLVM)"> diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile index 9b60f19899e..67ad2545764 100644 --- a/src/mono/sample/Android/Makefile +++ b/src/mono/sample/Android/Makefile @@ -35,6 +35,5 @@ run: /p:RunActivity=false \ '/p:RuntimeComponents="$(RUNTIME_COMPONENTS)"' \ '/p:DiagnosticPorts="$(DIAGNOSTIC_PORTS)"' - clean: rm -rf ../../../../artifacts/bin/AndroidSampleApp diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 17abac6fe73..61435f31aa9 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -231,6 +231,11 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task /// public string? ToolPrefix { get; set; } + /// + /// Prepends a prefix to the name of the assembler (as) tool ran by the AOT compiler. + /// + public string? AsPrefix { get; set; } + /// /// Path to the directory where msym artifacts are stored. /// @@ -723,6 +728,11 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task aotArgs.Add($"tool-prefix={ToolPrefix}"); } + if (!string.IsNullOrEmpty(AsPrefix)) + { + aotArgs.Add($"as-prefix={AsPrefix}"); + } + string assemblyFilename = Path.GetFileName(assembly); if (isDedup) @@ -845,7 +855,11 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task ProxyFile proxyFile = _cache.NewFile(llvmObjectFile); proxyFiles.Add(proxyFile); aotArgs.Add($"llvm-outfile={proxyFile.TempFile}"); - aotAssembly.SetMetadata("LlvmObjectFile", proxyFile.TargetFile); + + if (UseStaticLinking) + { + aotAssembly.SetMetadata("LlvmObjectFile", proxyFile.TargetFile); + } } } diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index b4a63116ed8..f4a57d478f8 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -345,6 +345,15 @@ internal static class Utils } #endif + public static bool IsWindows() + { +#if NETCOREAPP + return OperatingSystem.IsWindows(); +#else + return true; +#endif + } + public static bool IsManagedAssembly(string filePath) { if (!File.Exists(filePath)) diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs b/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs new file mode 100644 index 00000000000..6c3010a7eb3 --- /dev/null +++ b/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Android.Build.Ndk +{ + public sealed class AndroidArch(string archName, string abi, string triple) + { + public string ArchName { get; set; } = archName; + + public string Abi { get; set; } = abi; + + public string Triple { get; set; } = triple; + } +} diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs b/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs new file mode 100644 index 00000000000..b0e242353cc --- /dev/null +++ b/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + + +namespace Microsoft.Android.Build.Ndk +{ + public sealed class Ndk + { + private static string ndkPath = ""; + private static NdkVersion? ndkVersion; + + public static string NdkPath + { + get + { + if (string.IsNullOrEmpty(ndkPath)) + { + ndkPath = GetNdkPath(GetProbingPaths()); + } + + return ndkPath!; + } + } + + public static NdkVersion NdkVersion + { + get + { +#pragma warning disable IDE0074 // Use compound assignment + if (ndkVersion == null) + { + ndkVersion = ReadVersion(); + } +#pragma warning restore IDE0074 + + return ndkVersion; + } + } + + private static string GetNdkPath(IEnumerable probingPaths) + { + string ret = ""; + + foreach(string path in probingPaths) + { + if (Directory.Exists(path)) + { + ret = path; + break; + } + } + + return ret; + } + + private static List GetProbingPaths() + { + List paths = new List(); + + string? ndkEnvPath = Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT"); + + string[] fixedNdkPaths = (Utils.IsWindows()) ? + new string[] + { + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Android", "android-sdk", "ndk-bundle"), + Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "Android", "android-sdk-windows", "ndk-bundle"), + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ProgramW6432")) + ? Path.Combine(Environment.GetEnvironmentVariable("ProgramW6432") ?? "", "Android", "android-sdk", "ndk-bundle") + : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Android", "android-sdk", "ndk-bundle"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Android", "android-sdk", "ndk-bundle"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Android", "android-sdk", "ndk-bundle"), + @"C:\android-sdk-windows" + } + : + new string[] + { + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Android", "sdk") + }; + + if (!string.IsNullOrEmpty(ndkEnvPath)) + { + paths.Add(ndkEnvPath!); + } + + paths.AddRange(fixedNdkPaths); + + return paths; + } + + private static NdkVersion ReadVersion() + { + string sourcePropertiesPath = Path.Combine(NdkPath, "source.properties"); + if (!File.Exists(sourcePropertiesPath)) + { + throw new Exception("Could not find NDK version information"); + } + + var splitChars = new char[] {'='}; + string? ver = null; + foreach(string l in File.ReadAllLines(sourcePropertiesPath)) + { + string line = l.Trim (); + if (!line.StartsWith("Pkg.Revision", StringComparison.Ordinal)) + { + continue; + } + + string[] parts = line.Split(splitChars, 2); + if (parts.Length != 2) + { + throw new Exception($"Invalid NDK version format in '{sourcePropertiesPath}'"); + } + + ver = parts [1].Trim(); + } + + return new NdkVersion(ver); + } + } +} diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs new file mode 100644 index 00000000000..7f682ba9da5 --- /dev/null +++ b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.Android.Build.Ndk +{ + public sealed class NdkTools + { + private string toolRootPath; + private string toolPrefixPath; + private string asPrefixPath; + + private string clangPath; + private string ldName; + private string ldPath; + + private string netArch; + private string hostOS; + private string apiLevel; + + private static readonly Dictionary validHosts = new Dictionary() + { + { "osx", "darwin-x86_64" }, + { "linux", "linux-x86_64" }, + { "windows", "windows-x86_64" } + }; + + private static readonly Dictionary validArches = new Dictionary() + { + { "arm", new AndroidArch("arm", "armeabi-v7a", "arm-linux-androideabi") }, + { "arm64", new AndroidArch("aarch64", "aarch64-v8a", "aarch64-linux-android") }, + { "x86", new AndroidArch("x86", "x86", "i686-linux-android") }, + { "x64", new AndroidArch("x86_64", "x86_64", "x86_64-linux-android") } + }; + + public NdkTools(string netArch, string hostOS, string apiLevel) + { + string cmdExt = Utils.IsWindows() ? ".cmd" : ""; + + this.netArch = netArch; + this.apiLevel = apiLevel; + + ValidateRequiredProps(hostOS); + + this.hostOS = validHosts[hostOS]; + + toolRootPath = Path.Combine(Ndk.NdkPath, "toolchains", "llvm", "prebuilt", this.hostOS); + + asPrefixPath = Path.Combine(toolRootPath, Triple, "bin"); + toolPrefixPath = Path.Combine(toolRootPath, "bin"); + + clangPath = Path.Combine(ToolPrefixPath, $"{Triple}{apiLevel}-clang{cmdExt}"); + ldPath = toolPrefixPath; + ldName = "ld"; + } + + public string ToolPrefixPath + { + get => toolPrefixPath; + } + + public string AsPrefixPath + { + get => asPrefixPath; + } + + public string Triple + { + get => validArches[netArch].Triple; + } + + public string LdName + { + get => ldName; + } + + public string LdPath + { + get => ldPath; + } + + public string ClangPath + { + get => clangPath; + } + + private void ValidateRequiredProps(string hostOS) + { + if (Ndk.NdkVersion.Main.Major != 23) + { + throw new Exception($"NDK 23 is required. An unsupported NDK version was found ({Ndk.NdkVersion.Main.Major})."); + } + + try + { + string triple = Triple; + } + catch (KeyNotFoundException) + { + throw new Exception("An invalid target architecture was supplied. Only arm64, x64, arm, and x86 are supported."); + } + + try + { + string host = validHosts[hostOS]; + } + catch(KeyNotFoundException) + { + throw new Exception("An invalid HostOS value was supplied. Only windows, osx, and linux are supported."); + } + } + } +} diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs b/src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs new file mode 100644 index 00000000000..2db37152608 --- /dev/null +++ b/src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Android.Build.Ndk +{ + public class NdkVersion + { + public Version Main { get; } + public string Tag { get; } = ""; + + public NdkVersion() => Main = new Version(0, 0); + + public NdkVersion(string? version) + { + string? ver = version?.Trim(); + if (string.IsNullOrEmpty(ver)) + { + throw new ArgumentException ("must be a non-empty string", nameof (version)); + } + + int tagIdx = ver.IndexOf('-'); + if (tagIdx >= 0) + { + Tag = ver.Substring(tagIdx + 1); + ver = ver.Substring(0, tagIdx - 1); + } + + if (!Version.TryParse(ver, out Version? ndkVersion) || ndkVersion == null) + { + throw new InvalidOperationException ($"Failed to parse '{ver}' as a valid NDK version."); + } + + Main = ndkVersion; + } + + public override string ToString() + { + if (!string.IsNullOrEmpty(Tag)) + { + return $"{Main}-{Tag}"; + } + + return Main.ToString(); + } + } +} diff --git a/src/tasks/MobileBuildTasks/Android/Tasks/NdkToolFinder.cs b/src/tasks/MobileBuildTasks/Android/Tasks/NdkToolFinder.cs new file mode 100644 index 00000000000..9e252099bf1 --- /dev/null +++ b/src/tasks/MobileBuildTasks/Android/Tasks/NdkToolFinder.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Text; +using Microsoft.Android.Build.Ndk; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.Android.Build.Tasks +{ + public class NdkToolFinderTask : Task + { + /// + /// The dotnet specific target android architecture + /// + [Required] + [NotNull] + public string? Architecture { get; set; } + + /// + /// The dotnet specific host OS being used (windows, linux, osx) + /// + [Required] + [NotNull] + public string? HostOS { get; set; } + + /// + /// The minimum API level supported. This is important when using clang + /// + [Required] + [NotNull] + public string? MinApiLevel { get; set; } + + /// + /// The path to the folder that contains the android native assembler (as). + /// May not be supported in newer NDK versions. + /// + [Output] + public string? AsPrefixPath { get; set; } = ""!; + + /// + /// The path to the api level specific clang being used + /// + [Output] + public string? ClangPath { get; set; } = ""!; + + /// + /// The name of the linker being used. + /// + [Output] + public string? LdName { get; set; } = ""!; + + /// + /// The path to the linker being used + /// + [Output] + public string? LdPath { get; set; } = ""!; + + /// + /// The path to the NDK toolchain bin folder + /// + [Output] + public string? ToolPrefixPath { get; set; } = ""!; + + /// + /// The LLVM triple for the android target. + [Output] + public string? Triple { get; set; } = ""!; + + public override bool Execute() + { + NdkTools tools = new NdkTools(Architecture, HostOS, MinApiLevel); + AsPrefixPath = tools.AsPrefixPath; + ToolPrefixPath = tools.ToolPrefixPath; + Triple = tools.Triple; + LdName = tools.LdName; + LdPath = tools.LdPath; + ClangPath = tools.ClangPath; + + return true; + } + } +} diff --git a/src/tasks/MobileBuildTasks/MobileBuildTasks.csproj b/src/tasks/MobileBuildTasks/MobileBuildTasks.csproj new file mode 100644 index 00000000000..15398dfac4d --- /dev/null +++ b/src/tasks/MobileBuildTasks/MobileBuildTasks.csproj @@ -0,0 +1,35 @@ + + + $(TargetFrameworkForNETCoreTasks);$(TargetFrameworkForNETFrameworkTasks) + enable + $(NoWarn),CA1050 + + $(NoWarn),CS8604,CS8602 + $(NoWarn),CA1850 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj index 49ddce32380..46260897dde 100644 --- a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj @@ -3,6 +3,7 @@ Exe false true + true true $(NetCoreAppCurrent) Android.Device_Emulator.Aot_Llvm.Test.dll