From 0b3bc33caafbd7cfa96889a88c747955a83bc3d3 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 29 Jun 2023 15:41:17 -0400 Subject: [PATCH 01/12] [Android] Add NdkToolFinder task 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 | 69 ++++++---- src/mono/sample/Android/Makefile | 1 - src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 16 ++- src/tasks/Common/Utils.cs | 9 ++ .../Android/Ndk/AndroidArch.cs | 18 +++ 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, 549 insertions(+), 34 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 66120a0e9db699..9cbfefd631e7b7 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 31feac1cc2e054..0ed547d64cceed 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=")) { @@ -13112,6 +13115,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 @@ -13203,7 +13207,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); @@ -13214,7 +13218,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); @@ -14211,6 +14215,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 64fdebc6cef6bd..3c38f0c22dcd2b 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 0db739451372c2..8c3e7882df8dc3 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 e06a8c70a206d0..a4ce21967ad95e 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -10,8 +10,13 @@ - - + + + <_MobileIntermediateOutputPath>$(IntermediateOutputPath)mobile @@ -35,46 +40,54 @@ Condition="'$(ForceAOT)' == 'true'"/> - - arm64-v8a - armeabi-v7a - x86_64 - x86 - - + + <_AotOutputType>Library + <_AotLibraryFormat>So + + <_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)- + + 21 + + + + + + + + + + + + + <_AsPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_AsPrefixPath)')) + <_ToolPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_ToolPrefixPath)')) + ToolPrefix="$(_ToolPrefixPath)" + UseLLVM="$(UseLLVM)"> diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile index 9b60f19899e4e7..67ad25457644b3 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 93e47600a80cc5..7e01db5d8aefe2 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -220,6 +220,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. /// @@ -701,6 +706,11 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st aotArgs.Add($"tool-prefix={ToolPrefix}"); } + if (!string.IsNullOrEmpty(AsPrefix)) + { + aotArgs.Add($"as-prefix={AsPrefix}"); + } + string assemblyFilename = Path.GetFileName(assembly); if (isDedup) @@ -806,7 +816,11 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st 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 2f1cbd98fb3b66..6638162f810fbf 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -270,6 +270,15 @@ public static void DirectoryCopy(string sourceDir, string destDir, Func probingPaths) + { + string? ret = ""; + + foreach(string? path in probingPaths) + { + if (!string.IsNullOrEmpty(path) && 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 00000000000000..062c6a9fc6c8c5 --- /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("32-bit ARMv7", "arm", "armeabi-v7a", "arm-linux-androideabi") }, + { "arm64", new AndroidArch("64-bit ARMv8", "aarch64", "aarch64-v8a", "aarch64-linux-android") }, + { "x86", new AndroidArch("32-bit Intel", "x86", "x86", "i686-linux-android") }, + { "x64", new AndroidArch("64-bit Intel", "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 00000000000000..2db371526084ca --- /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 00000000000000..9e252099bf15b9 --- /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 00000000000000..15398dfac4dae4 --- /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 49ddce32380595..46260897ddecf2 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 From 0b80740118586ec8ec80200cb5d84d3a3bd90e96 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 5 Jul 2023 13:04:24 -0400 Subject: [PATCH 02/12] Feedback --- .../msbuild/android/build/AndroidBuild.props | 2 +- src/mono/sample/Android/AndroidSampleApp.csproj | 7 +++++-- .../MobileBuildTasks/Android/Ndk/AndroidArch.cs | 6 ++---- src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs | 16 ++++++++-------- .../MobileBuildTasks/Android/Ndk/NdkTools.cs | 8 ++++---- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/mono/msbuild/android/build/AndroidBuild.props b/src/mono/msbuild/android/build/AndroidBuild.props index 3c38f0c22dcd2b..15c82b729c140f 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.props +++ b/src/mono/msbuild/android/build/AndroidBuild.props @@ -4,7 +4,7 @@ true true - <_HostOS Condition="$([MSBuild]::IsOSPlatform(`Windows`))">windows + <_HostOS Condition="$([MSBuild]::IsOSPlatform('Windows'))">windows <_HostOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx <_HostOS Condition="'$(_HostOS)' == ''">linux diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index a4ce21967ad95e..30ff8ba31e93f7 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -46,11 +46,13 @@ <_AotOutputType>Library <_AotLibraryFormat>So + <_AotMode Condition="'$(AotMode)' == ''">Normal <_AotOutputType>AsmOnly <_AotModulesTablePath>$(ApkDir)\modules.c + <_AotMode Condition="'$(AotMode)' == ''">Full @@ -83,10 +85,11 @@ IntermediateOutputPath="$(IntermediateOutputPath)" LibraryFormat="$(_AotLibraryFormat)" LLVMPath="$(MonoAotCrossDir)" - Mode="Full" + Mode="$(_AotMode)" OutputDir="$(_MobileIntermediateOutputPath)" OutputType="$(_AotOutputType)" - ToolPrefix="$(_ToolPrefixPath)" + ToolPrefix="$(_ToolPrefixPath)" + UseAotDataFile="false" UseLLVM="$(UseLLVM)"> diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs b/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs index a623c310eb6db6..5f5c2d397edef3 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs @@ -5,10 +5,8 @@ namespace Microsoft.Android.Build.Ndk { - public sealed class AndroidArch(string name, string archName, string abi, string triple) - { - public string Name { get; set; } = name; - + public sealed class AndroidArch(string archName, string abi, string triple) + { public string ArchName { get; set; } = archName; public string Abi { get; set; } = abi; diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs b/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs index 33d97b6571289c..b0e242353cc90c 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs @@ -13,7 +13,7 @@ namespace Microsoft.Android.Build.Ndk { public sealed class Ndk { - private static string? ndkPath = ""; + private static string ndkPath = ""; private static NdkVersion? ndkVersion; public static string NdkPath @@ -44,13 +44,13 @@ public static NdkVersion NdkVersion } } - private static string? GetNdkPath(IEnumerable probingPaths) + private static string GetNdkPath(IEnumerable probingPaths) { - string? ret = ""; + string ret = ""; - foreach(string? path in probingPaths) + foreach(string path in probingPaths) { - if (!string.IsNullOrEmpty(path) && Directory.Exists(path)) + if (Directory.Exists(path)) { ret = path; break; @@ -60,9 +60,9 @@ public static NdkVersion NdkVersion return ret; } - private static List GetProbingPaths() + private static List GetProbingPaths() { - List paths = new List(); + List paths = new List(); string? ndkEnvPath = Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT"); @@ -86,7 +86,7 @@ public static NdkVersion NdkVersion if (!string.IsNullOrEmpty(ndkEnvPath)) { - paths.Add(ndkEnvPath); + paths.Add(ndkEnvPath!); } paths.AddRange(fixedNdkPaths); diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs index 062c6a9fc6c8c5..7f682ba9da5c8a 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs @@ -31,10 +31,10 @@ public sealed class NdkTools private static readonly Dictionary validArches = new Dictionary() { - { "arm", new AndroidArch("32-bit ARMv7", "arm", "armeabi-v7a", "arm-linux-androideabi") }, - { "arm64", new AndroidArch("64-bit ARMv8", "aarch64", "aarch64-v8a", "aarch64-linux-android") }, - { "x86", new AndroidArch("32-bit Intel", "x86", "x86", "i686-linux-android") }, - { "x64", new AndroidArch("64-bit Intel", "x86_64", "x86_64", "x86_64-linux-android") } + { "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) From c24527e38222220f4aca1e2e80f5a6c5267467d7 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 5 Jul 2023 13:22:24 -0400 Subject: [PATCH 03/12] Ws issue --- src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs b/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs index 5f5c2d397edef3..6c3010a7eb3a38 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/AndroidArch.cs @@ -6,7 +6,7 @@ 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; From dfae99057ccf81c9559f0cb79543c91e421ed4bb Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 13 Jul 2023 00:12:21 -0400 Subject: [PATCH 04/12] cmake-less android library build first pass --- src/mono/mono/mini/aot-compiler.c | 8 +- .../android/build/AndroidBuild.targets | 7 +- .../msbuild/common/LibraryBuilder.targets | 4 + .../AndroidAppBuilder.csproj | 4 +- src/tasks/AndroidAppBuilder/ApkBuilder.cs | 1 + .../Templates/monodroid-librarymode.c | 3 + src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 22 ++- src/tasks/Common/Builders/AndroidProject.cs | 91 --------- src/tasks/Common/Utils.cs | 18 ++ src/tasks/LibraryBuilder/LibraryBuilder.cs | 173 ++++++++++++++++-- .../LibraryBuilder/LibraryBuilder.csproj | 4 +- src/tasks/LibraryBuilder/Templates/autoinit.c | 1 + .../Android/AndroidBuildOptions.cs | 25 +++ .../Android/AndroidProject.cs | 167 +++++++++++++++++ .../TestExclusionListTasks.csproj | 4 +- ...droid.Device_Emulator.Aot_Llvm.Test.csproj | 2 +- 16 files changed, 417 insertions(+), 117 deletions(-) delete mode 100644 src/tasks/Common/Builders/AndroidProject.cs create mode 100644 src/tasks/MobileBuildTasks/Android/AndroidBuildOptions.cs create mode 100644 src/tasks/MobileBuildTasks/Android/AndroidProject.cs diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 7873a3cf369592..4e87c3355952a1 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -8761,6 +8761,8 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) { GPtrArray* args; + //opts->save_temps = FALSE; + args = mono_aot_split_options (aot_options ? aot_options : ""); for (guint i = 0; i < args->len; ++i) { const char *arg = (const char *)g_ptr_array_index (args, i); @@ -15339,7 +15341,11 @@ emit_aot_image (MonoAotCompile *acfg) acfg->tmpbasename = g_build_filename (temp_path, "temp", (const char*)NULL); acfg->tmpfname = g_strdup_printf ("%s.s", acfg->tmpbasename); acfg->llvm_sfile = g_strdup_printf ("%s-llvm.s", acfg->tmpbasename); - acfg->llvm_ofile = g_strdup_printf ("%s-llvm." AS_OBJECT_FILE_SUFFIX, acfg->tmpbasename); + + if (acfg->aot_opts.static_link) + acfg->llvm_ofile = g_strdup (acfg->aot_opts.llvm_outfile); + else + acfg->llvm_ofile = g_strdup_printf ("%s-llvm." AS_OBJECT_FILE_SUFFIX, acfg->tmpbasename); g_free (temp_path); } diff --git a/src/mono/msbuild/android/build/AndroidBuild.targets b/src/mono/msbuild/android/build/AndroidBuild.targets index 7d6f1504812fac..f1d7de63b62402 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.targets +++ b/src/mono/msbuild/android/build/AndroidBuild.targets @@ -3,7 +3,7 @@ $(GenerateAppBundle) true - false + false @@ -74,6 +74,7 @@ + @@ -110,6 +111,7 @@ <_UsesRuntimeInitCallback>$(UsesRuntimeInitCallback) <_UsesRuntimeInitCallback Condition="'$(_UsesRuntimeInitCallback)' == ''">true + <_AotOutputType>ObjectFile @@ -142,7 +144,6 @@ @@ -154,7 +155,7 @@ - + <_AsPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_AsPrefixPath)')) <_ToolPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_ToolPrefixPath)')) diff --git a/src/mono/msbuild/common/LibraryBuilder.targets b/src/mono/msbuild/common/LibraryBuilder.targets index 9ea4a4562ee1e9..2ecbcf00dfe211 100644 --- a/src/mono/msbuild/common/LibraryBuilder.targets +++ b/src/mono/msbuild/common/LibraryBuilder.targets @@ -24,6 +24,10 @@ <_ExtraLinkerArgs Include="@(_CommonLinkerArgs)" /> <_BundledRuntimeConfig Include="@(_BundledResources)" Condition="$([System.String]::Copy('%(Identity)').EndsWith('runtimeconfig.bin'))" /> + + <_ExtraLinkerArgs Include="-l:libz.so;-l:liblog.so;-l:libc.so;-l:libandroid.so;-l:libm.so;" /> + <_ExtraLinkerArgs Include="--build-id=sha1" /> + + + + - diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 650281be454b5a..98188fdbe919c6 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; +using Microsoft.Android.Build; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c b/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c index fed18863d6cd40..a50aa9cbe483db 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c @@ -53,5 +53,8 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_ setenv ("TMPDIR", cache_dir, true); setenv ("TEST_RESULTS_DIR", testresults_dir, true); + setenv ("MONO_LOG_LEVEL", "debug", true); + setenv ("MONO_LOG_MASK", "all", true); + return invoke_netlibrary_entrypoints (); } diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 61435f31aa9b66..a7c8518317d1f3 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -413,7 +413,7 @@ private bool ProcessAndValidateArguments() throw new LogAsErrorException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'."); } - if (!string.IsNullOrEmpty(AotModulesTablePath)) + if (!string.IsNullOrEmpty(AotModulesTablePath) || parsedOutputType == MonoAotOutputType.ObjectFile) { // AOT modules for static linking, needs the aot modules table UseStaticLinking = true; @@ -863,6 +863,20 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st } } + if (!string.IsNullOrEmpty(TempPath)) + { + aotArgs.Add($"temp-path={TempPath}"); + } + else if (!string.IsNullOrEmpty(IntermediateOutputPath)) + { + string aotTmpPath = Path.Combine(IntermediateOutputPath, assemblyFilename + ".tmp"); + if (!Directory.Exists(aotTmpPath)) + { + Directory.CreateDirectory(aotTmpPath); + } + aotArgs.Add($"temp-path={aotTmpPath}"); + } + if (EnableUnmanagedCallersOnlyMethodsExport) { string exportSymbolsFile = Path.Combine(OutputDir, Path.ChangeExtension(assemblyFilename, ".exportsymbols")); @@ -896,7 +910,6 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st } } - if (AotProfilePath?.Length > 0) { aotArgs.Add("profile-only"); @@ -920,11 +933,6 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st aotArgs.Add(AotArguments); } - if (!string.IsNullOrEmpty(TempPath)) - { - aotArgs.Add($"temp-path={TempPath}"); - } - if (!string.IsNullOrEmpty(LdName)) { aotArgs.Add($"ld-name={LdName}"); diff --git a/src/tasks/Common/Builders/AndroidProject.cs b/src/tasks/Common/Builders/AndroidProject.cs deleted file mode 100644 index 7620cda6b9abd0..00000000000000 --- a/src/tasks/Common/Builders/AndroidProject.cs +++ /dev/null @@ -1,91 +0,0 @@ -// 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.IO; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -internal sealed class AndroidProject -{ - private const string DefaultMinApiLevel = "21"; - private const string Cmake = "cmake"; - - private TaskLoggingHelper logger; - - private string abi; - private string androidToolchainPath; - private string projectName; - - public string Abi => abi; - - // set the project name to something generic. - // return the output path - // let the builder figure out the name + extension - - public AndroidProject(string projectName, string runtimeIdentifier, TaskLoggingHelper logger) : - this(projectName, runtimeIdentifier, Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT")!, logger) - { - } - - public AndroidProject(string projectName, string runtimeIdentifier, string androidNdkPath, TaskLoggingHelper logger) - { - androidToolchainPath = Path.Combine(androidNdkPath, "build", "cmake", "android.toolchain.cmake"); - abi = DetermineAbi(runtimeIdentifier); - - this.logger = logger; - this.projectName = projectName; - } - - public void GenerateCMake(string workingDir, bool stripDebugSymbols) - { - GenerateCMake(workingDir, DefaultMinApiLevel, stripDebugSymbols); - } - - public void GenerateCMake(string workingDir, string apiLevel = DefaultMinApiLevel, bool stripDebugSymbols = false) - { - string cmakeGenArgs = $"-DCMAKE_TOOLCHAIN_FILE={androidToolchainPath} -DANDROID_ABI=\"{Abi}\" -DANDROID_STL=none -DTARGETS_ANDROID=1 " + - $"-DANDROID_PLATFORM=android-{apiLevel} -B {projectName}"; - - if (stripDebugSymbols) - { - // Use "-s" to strip debug symbols, it complains it's unused but it works - cmakeGenArgs += " -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_C_FLAGS=\"-s -Wno-unused-command-line-argument\""; - } - else - { - cmakeGenArgs += " -DCMAKE_BUILD_TYPE=Debug"; - } - - Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeGenArgs); - } - - public string BuildCMake(string workingDir, bool stripDebugSymbols = false) - { - string cmakeBuildArgs = $"--build {projectName}"; - - if (stripDebugSymbols) - { - cmakeBuildArgs += " --config MinSizeRel"; - } - else - { - cmakeBuildArgs += " --config Debug"; - } - - Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeBuildArgs); - - return Path.Combine(workingDir, projectName); - } - - private static string DetermineAbi(string runtimeIdentifier) => - runtimeIdentifier switch - { - "android-x86" => "x86", - "android-x64" => "x86_64", - "android-arm" => "armeabi-v7a", - "android-arm64" => "arm64-v8a", - _ => throw new ArgumentException($"{runtimeIdentifier} is not supported for Android"), - }; -} diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index f4a57d478f85b4..eca2ccd9740233 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -354,6 +354,24 @@ public static bool IsWindows() #endif } + public static bool IsMacOS() + { +#if NETCOREAPP + return OperatingSystem.IsMacOS(); +#else + return false; +#endif + } + + public static bool IsLinux() + { +#if NETCOREAPP + return OperatingSystem.IsLinux(); +#else + return false; +#endif + } + public static bool IsManagedAssembly(string filePath) { if (!File.Exists(filePath)) diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.cs b/src/tasks/LibraryBuilder/LibraryBuilder.cs index e53a7f7c366478..b94b492e44fadb 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.cs +++ b/src/tasks/LibraryBuilder/LibraryBuilder.cs @@ -9,6 +9,7 @@ using System.Text; using System.Linq; using System.Runtime.InteropServices; +using Microsoft.Android.Build; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -137,6 +138,10 @@ public override bool Execute() StringBuilder extraSources = new StringBuilder(); StringBuilder linkerArgs = new StringBuilder(); + List sources = new List(); + List libs = new List(); + List lArgs = new List(); + if (!ValidateValidTargetOS()) { throw new ArgumentException($"{TargetOS} is not yet supported by the librarybuilder task."); @@ -148,26 +153,124 @@ public override bool Execute() return false; } - GatherAotSourcesObjects(aotSources, aotObjects, extraSources, linkerArgs); - GatherLinkerArgs(linkerArgs); + if (TargetOS == "android") + { + File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), + Utils.GetEmbeddedResource("library-builder.h")); - File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), - Utils.GetEmbeddedResource("library-builder.h")); + GenerateAssembliesLoader(); - GenerateAssembliesLoader(); + if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) + { + WriteAutoInitializationFromTemplate(); + } - if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) - { - WriteAutoInitializationFromTemplate(); - extraSources.AppendLine(" autoinit.c"); + GatherSourcesAndLibs(sources, libs, lArgs); + OutputPath = BuildLibrary(sources, libs, lArgs); } + else + { + GatherAotSourcesObjects(aotSources, aotObjects, extraSources, linkerArgs); + GatherLinkerArgs(linkerArgs); + + File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), + Utils.GetEmbeddedResource("library-builder.h")); - WriteCMakeFileFromTemplate(aotSources.ToString(), aotObjects.ToString(), extraSources.ToString(), linkerArgs.ToString()); - OutputPath = BuildLibrary(); + GenerateAssembliesLoader(); + + if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) + { + WriteAutoInitializationFromTemplate(); + extraSources.AppendLine(" autoinit.c"); + } + + WriteCMakeFileFromTemplate(aotSources.ToString(), aotObjects.ToString(), extraSources.ToString(), linkerArgs.ToString()); + OutputPath = BuildLibrary(); + } return true; } + private void GatherSourcesAndLibs(List sources, List libs, List linkerArgs) + { + List exportedSymbols = new List(); + + foreach (CompiledAssembly compiledAssembly in CompiledAssemblies) + { + if (!usesAOTDataFile && !string.IsNullOrEmpty(compiledAssembly.DataFile)) + { + usesAOTDataFile = true; + } + + if (!string.IsNullOrEmpty(compiledAssembly.LlvmObjectFile)) + { + sources.Add(compiledAssembly.LlvmObjectFile); + } + + if (!string.IsNullOrEmpty(compiledAssembly.ObjectFile)) + { + sources.Add(compiledAssembly.ObjectFile); + } + + if (!string.IsNullOrEmpty(compiledAssembly.ExportsFile)) + { + int symbolsAdded = GatherExportedSymbols(compiledAssembly.ExportsFile, exportedSymbols); + + if (symbolsAdded > 0) + { + exportedAssemblies.Add(Path.GetFileName(compiledAssembly.Path)); + } + } + } + + foreach (ITaskItem lib in RuntimeLibraries) + { + string ext = Path.GetExtension(lib.ItemSpec); + + if (ext == ".so" || ext == ".dylib") + { + libs.Add(lib.ItemSpec); + } + else + { + sources.Add(lib.ItemSpec); + } + } + + foreach (ITaskItem item in ExtraLinkerArguments) + { + linkerArgs.Add(item.ItemSpec); + } + + if (exportedAssemblies.Count == 0) + { + throw new LogAsErrorException($"None of the compiled assemblies contain exported symbols. The library must export only symbols resulting from [UnmanageCallersOnly(Entrypoint = )]Resulting shared library would be unusable."); + } + + if (IsSharedLibrary) + { + // for android, all symbols to keep go in one linker script + // + // for ios, multiple files can be specified + if (TargetOS == "android") + { + WriteLinkerScriptFile(MobileSymbolFileName, exportedSymbols); + linkerArgs.Add($"\"--version-script={MobileSymbolFileName}\""); + } + else + { + throw new NotImplementedException("iOS coming"); + } + } + + foreach (ITaskItem item in ExtraSources) + { + sources.Add(item.ItemSpec); + } + + ExportedSymbols = exportedSymbols.ToArray(); + } + private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aotObjects, StringBuilder extraSources, StringBuilder linkerArgs) { List exportedSymbols = new List(); @@ -319,6 +422,10 @@ private void WriteAutoInitializationFromTemplate() .Replace("%RUNTIME_CONFIG_DATA%", dataSymbol) .Replace("%RUNTIME_CONFIG_DATA_LEN%", dataLenSymbol); } + else + { + autoInitialization = autoInitialization.Replace("%EXTERN_BUNDLED_RESOURCES_SYMBOLS%", string.Empty); + } File.WriteAllText(Path.Combine(OutputDirectory, "autoinit.c"), autoInitialization); } @@ -370,6 +477,50 @@ private string GenerateExtraDefinitions() return extraDefinitions.ToString(); } + private string BuildLibrary(List sources, List libs, List linkerArgs) + { + string libraryName = GetLibraryName(); + + if (TargetOS == "android") + { + AndroidBuildOptions buildOptions = new AndroidBuildOptions(); + buildOptions.CompilerArguments.Add("-D ANDROID=1"); + buildOptions.CompilerArguments.Add("-D HOST_ANDROID=1"); + buildOptions.CompilerArguments.Add("-fPIC"); + buildOptions.CompilerArguments.Add(IsSharedLibrary ? $"-shared -o {libraryName}" : $"-o {libraryName}"); + buildOptions.IncludePaths.Add(MonoRuntimeHeaders); + buildOptions.LinkerArguments.Add($"--soname={libraryName}"); + buildOptions.LinkerArguments.AddRange(linkerArgs); + buildOptions.NativeLibraryPaths.AddRange(libs); + buildOptions.Sources.AddRange(sources); + buildOptions.Sources.Add("preloaded-assemblies.c"); + + if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) + { + buildOptions.Sources.Add("autoinit.c"); + } + + if (BundlesResources) + { + buildOptions.CompilerArguments.Add("-D BUNDLED_RESOURCES=1"); + } + + if (usesAOTDataFile) + { + buildOptions.CompilerArguments.Add("-D USES_AOT_DATA=1"); + } + + AndroidProject project = new AndroidProject("netlibrary", RuntimeIdentifier, Log); + project.Build(OutputDirectory, buildOptions, StripDebugSymbols); + } + else + { + throw new NotImplementedException("iOS without CMake is not yet implemented"); + } + + return Path.Combine(OutputDirectory, libraryName); + } + private string BuildLibrary() { string libraryOutputPath; diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.csproj b/src/tasks/LibraryBuilder/LibraryBuilder.csproj index 60fa391055829e..e2b6ed159cf0df 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.csproj +++ b/src/tasks/LibraryBuilder/LibraryBuilder.csproj @@ -13,11 +13,13 @@ + + + - diff --git a/src/tasks/LibraryBuilder/Templates/autoinit.c b/src/tasks/LibraryBuilder/Templates/autoinit.c index e104345a434085..8e362cde77cbd7 100644 --- a/src/tasks/LibraryBuilder/Templates/autoinit.c +++ b/src/tasks/LibraryBuilder/Templates/autoinit.c @@ -45,6 +45,7 @@ initialize_runtimeconfig (const char *bundle_path) if (!arg->runtimeconfig.data.data) return; #else + char *file_name = "runtimeconfig.bin"; size_t str_len = sizeof (char) * (strlen (bundle_path) + strlen (file_name) + 2); // +1 "/", +1 null-terminating char char *file_path = (char *)malloc (str_len); if (!file_path) diff --git a/src/tasks/MobileBuildTasks/Android/AndroidBuildOptions.cs b/src/tasks/MobileBuildTasks/Android/AndroidBuildOptions.cs new file mode 100644 index 00000000000000..3893dd4de06a2b --- /dev/null +++ b/src/tasks/MobileBuildTasks/Android/AndroidBuildOptions.cs @@ -0,0 +1,25 @@ +// 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; + +namespace Microsoft.Android.Build +{ + public sealed class AndroidBuildOptions + { + public List CompilerArguments { get; } = new List(); + + public List IncludePaths { get; } = new List(); + + public string IntermediateOutputPath { get; set; } = string.Empty; + + public List LinkerArguments { get; } = new List(); + + public List NativeLibraryPaths { get; } = new List(); + + public string OutputPath { get; set; } = string.Empty; + + public List Sources { get; } = new List(); + } +} diff --git a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs new file mode 100644 index 00000000000000..4f2dc071ed0411 --- /dev/null +++ b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs @@ -0,0 +1,167 @@ +// 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.Text; +using Microsoft.Android.Build.Ndk; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.Android.Build +{ + public sealed class AndroidProject + { + private const string DefaultMinApiLevel = "21"; + private const string Cmake = "cmake"; + + private TaskLoggingHelper logger; + + private string abi; + private string androidToolchainPath; + private string projectName; + private string targetArchitecture; + + public string Abi => abi; + + // set the project name to something generic. + // return the output path + // let the builder figure out the name + extension + + public AndroidProject(string projectName, string runtimeIdentifier, TaskLoggingHelper logger) : + this(projectName, runtimeIdentifier, Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT")!, logger) + { + } + + public AndroidProject(string projectName, string runtimeIdentifier, string androidNdkPath, TaskLoggingHelper logger) + { + androidToolchainPath = Path.Combine(androidNdkPath, "build", "cmake", "android.toolchain.cmake"); + abi = DetermineAbi(runtimeIdentifier); + targetArchitecture = GetTargetArchitecture(runtimeIdentifier); + + logger.LogError("TargetArch: " + targetArchitecture); + + this.logger = logger; + this.projectName = projectName; + } + + public void Build(string workingDir, AndroidBuildOptions buildOptions, bool stripDebugSymbols = false, string apiLevel = DefaultMinApiLevel) + { + NdkTools tools = new NdkTools(targetArchitecture, GetHostOS(), apiLevel); + + string clangArgs = BuildClangArgs(buildOptions); + Utils.RunProcess(logger, tools.ClangPath, workingDir: workingDir, args: clangArgs); + } + + public void GenerateCMake(string workingDir, bool stripDebugSymbols) + { + GenerateCMake(workingDir, DefaultMinApiLevel, stripDebugSymbols); + } + + public void GenerateCMake(string workingDir, string apiLevel = DefaultMinApiLevel, bool stripDebugSymbols = false) + { + string cmakeGenArgs = $"-DCMAKE_TOOLCHAIN_FILE={androidToolchainPath} -DANDROID_ABI=\"{Abi}\" -DANDROID_STL=none -DTARGETS_ANDROID=1 " + + $"-DANDROID_PLATFORM=android-{apiLevel} -B {projectName}"; + + if (stripDebugSymbols) + { + // Use "-s" to strip debug symbols, it complains it's unused but it works + cmakeGenArgs += " -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_C_FLAGS=\"-s -Wno-unused-command-line-argument\""; + } + else + { + cmakeGenArgs += " -DCMAKE_BUILD_TYPE=Debug"; + } + + Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeGenArgs); + } + + public string BuildCMake(string workingDir, bool stripDebugSymbols = false) + { + string cmakeBuildArgs = $"--build {projectName}"; + + if (stripDebugSymbols) + { + cmakeBuildArgs += " --config MinSizeRel"; + } + else + { + cmakeBuildArgs += " --config Debug"; + } + + Utils.RunProcess(logger, Cmake, workingDir: workingDir, args: cmakeBuildArgs); + + return Path.Combine(workingDir, projectName); + } + + private static string BuildClangArgs(AndroidBuildOptions buildOptions) + { + StringBuilder ret = new StringBuilder(); + + foreach(string compilerArg in buildOptions.CompilerArguments) + { + ret.Append(compilerArg); + ret.Append(' '); + } + + foreach(string includeDir in buildOptions.IncludePaths) + { + ret.Append($"-I {includeDir} "); + } + + foreach(string linkerArg in buildOptions.LinkerArguments) + { + ret.Append($"-Xlinker {linkerArg} "); + } + + foreach(string source in buildOptions.Sources) + { + ret.Append(source); + ret.Append(' '); + } + + HashSet libDirs = new HashSet(); + foreach(string lib in buildOptions.NativeLibraryPaths) + { + string rootPath = Path.GetDirectoryName(lib)!; + string libName = Path.GetFileName(lib); + + if (!libDirs.Contains(rootPath)) + { + libDirs.Add(rootPath); + ret.Append($"-L {rootPath} "); + } + ret.Append($"-l:{libName} "); + } + + return ret.ToString(); + } + + private static string GetTargetArchitecture(string runtimeIdentifier) + { + int pos = runtimeIdentifier.IndexOf('-'); + return (pos > -1) ? runtimeIdentifier.Substring(pos + 1) : ""; + } + + private static string GetHostOS() + { + if (Utils.IsWindows()) + return "windows"; + else if (Utils.IsMacOS()) + return "osx"; + else + return "linux"; + } + + private static string DetermineAbi(string runtimeIdentifier) => + runtimeIdentifier switch + { + "android-x86" => "x86", + "android-x64" => "x86_64", + "android-arm" => "armeabi-v7a", + "android-arm64" => "arm64-v8a", + _ => throw new ArgumentException($"{runtimeIdentifier} is not supported for Android"), + }; + } +} diff --git a/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj b/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj index 2fad90bd306179..592a8652953b83 100644 --- a/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj +++ b/src/tasks/TestExclusionListTasks/TestExclusionListTasks.csproj @@ -11,10 +11,12 @@ - + + + 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 46260897ddecf2..6d5d4f3bcb5fb0 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,13 +3,13 @@ Exe false true - true true $(NetCoreAppCurrent) Android.Device_Emulator.Aot_Llvm.Test.dll 42 true true + false From 53aa40f91e714ec052667ea6dc58cd4e8459dbc2 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 13 Jul 2023 22:25:44 -0400 Subject: [PATCH 05/12] Change Android library mode to use the native toolchain as opposed to cmake. --- src/mono/mono/mini/aot-compiler.c | 2 - .../android/build/AndroidBuild.targets | 6 +- .../msbuild/common/LibraryBuilder.targets | 4 - .../Templates/monodroid-librarymode.c | 4 +- src/tasks/LibraryBuilder/LibraryBuilder.cs | 129 +++++++----------- .../Android/AndroidProject.cs | 7 +- .../Device_Emulator/AOT_LLVM/Program.cs | 7 - ...id.Device_Emulator.LibraryMode.Test.csproj | 25 ++++ .../ILLink.Descriptors.xml | 2 +- .../Device_Emulator/LibraryMode/Program.cs | 20 +++ 10 files changed, 100 insertions(+), 106 deletions(-) create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj rename src/tests/FunctionalTests/Android/Device_Emulator/{AOT_LLVM => LibraryMode}/ILLink.Descriptors.xml (62%) create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 4e87c3355952a1..3a37841a8069c3 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -8761,8 +8761,6 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) { GPtrArray* args; - //opts->save_temps = FALSE; - args = mono_aot_split_options (aot_options ? aot_options : ""); for (guint i = 0; i < args->len; ++i) { const char *arg = (const char *)g_ptr_array_index (args, i); diff --git a/src/mono/msbuild/android/build/AndroidBuild.targets b/src/mono/msbuild/android/build/AndroidBuild.targets index f1d7de63b62402..c8eed9b6fa72f9 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.targets +++ b/src/mono/msbuild/android/build/AndroidBuild.targets @@ -2,8 +2,7 @@ $(GenerateAppBundle) true - - + false false @@ -47,6 +46,9 @@ + <_CommonLinkerArgs Include="-l:libz.so;-l:liblog.so;-l:libc.so;-l:libm.so;" /> + <_CommonLinkerArgs Include="--build-id=sha1" /> + diff --git a/src/mono/msbuild/common/LibraryBuilder.targets b/src/mono/msbuild/common/LibraryBuilder.targets index 2ecbcf00dfe211..9ea4a4562ee1e9 100644 --- a/src/mono/msbuild/common/LibraryBuilder.targets +++ b/src/mono/msbuild/common/LibraryBuilder.targets @@ -24,10 +24,6 @@ <_ExtraLinkerArgs Include="@(_CommonLinkerArgs)" /> <_BundledRuntimeConfig Include="@(_BundledResources)" Condition="$([System.String]::Copy('%(Identity)').EndsWith('runtimeconfig.bin'))" /> - - <_ExtraLinkerArgs Include="-l:libz.so;-l:liblog.so;-l:libc.so;-l:libandroid.so;-l:libm.so;" /> - <_ExtraLinkerArgs Include="--build-id=sha1" /> - sources = new List(); - List libs = new List(); - List lArgs = new List(); - if (!ValidateValidTargetOS()) { throw new ArgumentException($"{TargetOS} is not yet supported by the librarybuilder task."); @@ -155,6 +151,12 @@ public override bool Execute() if (TargetOS == "android") { + List sources = new List(); + List libs = new List(); + List lArgs = new List(); + + GatherSourcesAndLibs(sources, libs, lArgs); + File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), Utils.GetEmbeddedResource("library-builder.h")); @@ -165,7 +167,6 @@ public override bool Execute() WriteAutoInitializationFromTemplate(); } - GatherSourcesAndLibs(sources, libs, lArgs); OutputPath = BuildLibrary(sources, libs, lArgs); } else @@ -191,6 +192,7 @@ public override bool Execute() return true; } + // Intended for native toolchain specific builds private void GatherSourcesAndLibs(List sources, List libs, List linkerArgs) { List exportedSymbols = new List(); @@ -252,15 +254,8 @@ private void GatherSourcesAndLibs(List sources, List libs, List< // for android, all symbols to keep go in one linker script // // for ios, multiple files can be specified - if (TargetOS == "android") - { - WriteLinkerScriptFile(MobileSymbolFileName, exportedSymbols); - linkerArgs.Add($"\"--version-script={MobileSymbolFileName}\""); - } - else - { - throw new NotImplementedException("iOS coming"); - } + WriteLinkerScriptFile(MobileSymbolFileName, exportedSymbols); + linkerArgs.Add($"\"--version-script={MobileSymbolFileName}\""); } foreach (ITaskItem item in ExtraSources) @@ -271,6 +266,7 @@ private void GatherSourcesAndLibs(List sources, List libs, List< ExportedSymbols = exportedSymbols.ToArray(); } + // intended for cmake based builds private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aotObjects, StringBuilder extraSources, StringBuilder linkerArgs) { List exportedSymbols = new List(); @@ -310,22 +306,11 @@ private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aot if (IsSharedLibrary) { - // for android, all symbols to keep go in one linker script - // - // for ios, multiple files can be specified - if (TargetOS == "android") - { - WriteLinkerScriptFile(MobileSymbolFileName, exportedSymbols); - WriteLinkerScriptArg(MobileSymbolFileName, linkerArgs); - } - else - { - File.WriteAllText( - MobileSymbolFileName, - string.Join("\n", exportedSymbols.Select(symbol => symbol)) - ); - WriteExportedSymbolsArg(MobileSymbolFileName, linkerArgs); - } + File.WriteAllText( + MobileSymbolFileName, + string.Join("\n", exportedSymbols.Select(symbol => symbol)) + ); + WriteExportedSymbolsArg(MobileSymbolFileName, linkerArgs); } foreach (ITaskItem item in ExtraSources) @@ -338,12 +323,7 @@ private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aot private void GatherLinkerArgs(StringBuilder linkerArgs) { - string libForceLoad = ""; - - if (TargetOS != "android") - { - libForceLoad = "-force_load "; - } + string libForceLoad = "-force_load "; foreach (ITaskItem item in RuntimeLibraries) { @@ -477,68 +457,53 @@ private string GenerateExtraDefinitions() return extraDefinitions.ToString(); } + // For now Android only using the NDK toolchain private string BuildLibrary(List sources, List libs, List linkerArgs) { string libraryName = GetLibraryName(); - if (TargetOS == "android") - { - AndroidBuildOptions buildOptions = new AndroidBuildOptions(); - buildOptions.CompilerArguments.Add("-D ANDROID=1"); - buildOptions.CompilerArguments.Add("-D HOST_ANDROID=1"); - buildOptions.CompilerArguments.Add("-fPIC"); - buildOptions.CompilerArguments.Add(IsSharedLibrary ? $"-shared -o {libraryName}" : $"-o {libraryName}"); - buildOptions.IncludePaths.Add(MonoRuntimeHeaders); - buildOptions.LinkerArguments.Add($"--soname={libraryName}"); - buildOptions.LinkerArguments.AddRange(linkerArgs); - buildOptions.NativeLibraryPaths.AddRange(libs); - buildOptions.Sources.AddRange(sources); - buildOptions.Sources.Add("preloaded-assemblies.c"); + AndroidBuildOptions buildOptions = new AndroidBuildOptions(); + buildOptions.CompilerArguments.Add("-D ANDROID=1"); + buildOptions.CompilerArguments.Add("-D HOST_ANDROID=1"); + buildOptions.CompilerArguments.Add("-fPIC"); + buildOptions.CompilerArguments.Add(IsSharedLibrary ? $"-shared -o {libraryName}" : $"-o {libraryName}"); + buildOptions.IncludePaths.Add(MonoRuntimeHeaders); + buildOptions.LinkerArguments.Add($"--soname={libraryName}"); + buildOptions.LinkerArguments.AddRange(linkerArgs); + buildOptions.NativeLibraryPaths.AddRange(libs); + buildOptions.Sources.AddRange(sources); + buildOptions.Sources.Add("preloaded-assemblies.c"); - if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) - { - buildOptions.Sources.Add("autoinit.c"); - } - - if (BundlesResources) - { - buildOptions.CompilerArguments.Add("-D BUNDLED_RESOURCES=1"); - } - - if (usesAOTDataFile) - { - buildOptions.CompilerArguments.Add("-D USES_AOT_DATA=1"); - } + if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) + { + buildOptions.Sources.Add("autoinit.c"); + } - AndroidProject project = new AndroidProject("netlibrary", RuntimeIdentifier, Log); - project.Build(OutputDirectory, buildOptions, StripDebugSymbols); + if (BundlesResources) + { + buildOptions.CompilerArguments.Add("-D BUNDLED_RESOURCES=1"); } - else + + if (usesAOTDataFile) { - throw new NotImplementedException("iOS without CMake is not yet implemented"); + buildOptions.CompilerArguments.Add("-D USES_AOT_DATA=1"); } + AndroidProject project = new AndroidProject("netlibrary", RuntimeIdentifier, Log); + project.Build(OutputDirectory, buildOptions, StripDebugSymbols); + return Path.Combine(OutputDirectory, libraryName); } + // iOS using CMake private string BuildLibrary() { string libraryOutputPath; + Xcode project = new Xcode(Log, RuntimeIdentifier); + project.CreateXcodeProject("netlibrary", OutputDirectory); - if (TargetOS == "android") - { - AndroidProject project = new AndroidProject("netlibrary", RuntimeIdentifier, Log); - project.GenerateCMake(OutputDirectory, StripDebugSymbols); - libraryOutputPath = project.BuildCMake(OutputDirectory, StripDebugSymbols); - } - else - { - Xcode project = new Xcode(Log, RuntimeIdentifier); - project.CreateXcodeProject("netlibrary", OutputDirectory); - - string xcodeProjectPath = Path.Combine(OutputDirectory, "netlibrary", $"{Name}.xcodeproj"); - libraryOutputPath = project.BuildAppBundle(xcodeProjectPath, StripDebugSymbols, "-"); - } + string xcodeProjectPath = Path.Combine(OutputDirectory, "netlibrary", $"{Name}.xcodeproj"); + libraryOutputPath = project.BuildAppBundle(xcodeProjectPath, StripDebugSymbols, "-"); return Path.Combine(libraryOutputPath, GetLibraryName()); } @@ -564,7 +529,7 @@ private string GetLibraryName() private bool ValidateValidTargetOS() => TargetOS switch { - "android" or "ios" or "tvos" or "maccatalyst" => true, + "android" or "ios" or "iossimulator" or "tvos" or "tvossimulator" or "maccatalyst" => true, _ => false }; } diff --git a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs index 4f2dc071ed0411..eb41d452333395 100644 --- a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs +++ b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs @@ -25,10 +25,6 @@ public sealed class AndroidProject public string Abi => abi; - // set the project name to something generic. - // return the output path - // let the builder figure out the name + extension - public AndroidProject(string projectName, string runtimeIdentifier, TaskLoggingHelper logger) : this(projectName, runtimeIdentifier, Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT")!, logger) { @@ -40,12 +36,11 @@ public AndroidProject(string projectName, string runtimeIdentifier, string andro abi = DetermineAbi(runtimeIdentifier); targetArchitecture = GetTargetArchitecture(runtimeIdentifier); - logger.LogError("TargetArch: " + targetArchitecture); - this.logger = logger; this.projectName = projectName; } + // builds using NDK toolchain public void Build(string workingDir, AndroidBuildOptions buildOptions, bool stripDebugSymbols = false, string apiLevel = DefaultMinApiLevel) { NdkTools tools = new NdkTools(targetArchitecture, GetHostOS(), apiLevel); diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs index 916d1f1aa03b80..9587bb1b57f80a 100644 --- a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs +++ b/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/Program.cs @@ -2,16 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Runtime.InteropServices; public static class Program { - [UnmanagedCallersOnly(EntryPoint = nameof(SayHello))] - public static void SayHello() - { - Console.WriteLine("Called from native! Hello!"); - } - public static int Main() { Console.WriteLine("Hello, Android!"); // logcat diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj new file mode 100644 index 00000000000000..da11e1ea6116af --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj @@ -0,0 +1,25 @@ + + + Exe + true + false + true + true + shared + true + $(NetCoreAppCurrent) + Android.Device_Emulator.LibraryMode.Test.dll + 42 + true + true + + + + + + + + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/ILLink.Descriptors.xml b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/ILLink.Descriptors.xml similarity index 62% rename from src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/ILLink.Descriptors.xml rename to src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/ILLink.Descriptors.xml index b1259ec37acd97..7fbe2a5cde589f 100644 --- a/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM/ILLink.Descriptors.xml +++ b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/ILLink.Descriptors.xml @@ -1,5 +1,5 @@ - + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs new file mode 100644 index 00000000000000..916d1f1aa03b80 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; + +public static class Program +{ + [UnmanagedCallersOnly(EntryPoint = nameof(SayHello))] + public static void SayHello() + { + Console.WriteLine("Called from native! Hello!"); + } + + public static int Main() + { + Console.WriteLine("Hello, Android!"); // logcat + return 42; + } +} From 21ee15ccb51bbf24a78df5d96d4b3e6aa303aac4 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 13 Jul 2023 22:35:31 -0400 Subject: [PATCH 06/12] Fix conflict --- src/tasks/Common/Utils.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index ec8fa5da75075f..eca2ccd9740233 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -354,7 +354,6 @@ public static bool IsWindows() #endif } -<<<<<<< HEAD public static bool IsMacOS() { #if NETCOREAPP @@ -373,8 +372,6 @@ public static bool IsLinux() #endif } -======= ->>>>>>> upstream/main public static bool IsManagedAssembly(string filePath) { if (!File.Exists(filePath)) From 59dc916415240959d676eecf0e44fb8c3045e123 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Fri, 14 Jul 2023 11:16:29 -0400 Subject: [PATCH 07/12] Remove xml descriptor reference --- .../AOT_LLVM/Android.Device_Emulator.Aot_Llvm.Test.csproj | 5 ----- 1 file changed, 5 deletions(-) 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 440df888de8d88..531725b8976ebe 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 @@ -13,11 +13,6 @@ false - - - - - From 9b5dd00d10032d7b6a0026349e347399158df0a2 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Fri, 14 Jul 2023 13:38:46 -0400 Subject: [PATCH 08/12] Fix incorrect arm triple --- src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs index 7f682ba9da5c8a..9117bd2b4a9a00 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs @@ -31,7 +31,7 @@ public sealed class NdkTools private static readonly Dictionary validArches = new Dictionary() { - { "arm", new AndroidArch("arm", "armeabi-v7a", "arm-linux-androideabi") }, + { "arm", new AndroidArch("arm", "armeabi-v7a", "armv7a-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") } From 9e0fb8ffa5740aca6dae75bbd8295eeb848962eb Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 2 Aug 2023 14:21:15 -0400 Subject: [PATCH 09/12] [iOS] Remove cmake build dependency for library mode This change removes cmake as a build dependency and instead uses clang directly when building for iOS/tvOS/Macatalyst library mode. --- .../msbuild/apple/build/AppleBuild.targets | 35 ++- src/tasks/AppleAppBuilder/AppleAppBuilder.cs | 61 +++-- .../CMakeLists-librarymode.txt.template | 83 ++++++ .../Templates/runtime-librarymode.m | 81 ++++++ src/tasks/AppleAppBuilder/Xcode.cs | 144 +++++----- src/tasks/LibraryBuilder/LibraryBuilder.cs | 249 ++++++------------ .../Android/AndroidProject.cs | 5 +- .../MobileBuildTasks/Apple/AppleProject.cs | 162 ++++++++++++ src/tasks/MobileBuildTasks/Apple/AppleSdk.cs | 143 ++++++++++ .../ClangBuildOptions.cs} | 4 +- .../LibraryMode/ILLink.Descriptors.xml | 7 + .../iOS/Simulator/LibraryMode/Program.cs | 29 ++ .../iOS.Simulator.LibraryMode.Test.csproj | 29 ++ 13 files changed, 751 insertions(+), 281 deletions(-) create mode 100644 src/tasks/AppleAppBuilder/Templates/CMakeLists-librarymode.txt.template create mode 100644 src/tasks/AppleAppBuilder/Templates/runtime-librarymode.m create mode 100644 src/tasks/MobileBuildTasks/Apple/AppleProject.cs create mode 100644 src/tasks/MobileBuildTasks/Apple/AppleSdk.cs rename src/tasks/MobileBuildTasks/{Android/AndroidBuildOptions.cs => Clang/ClangBuildOptions.cs} (89%) create mode 100644 src/tests/FunctionalTests/iOS/Simulator/LibraryMode/ILLink.Descriptors.xml create mode 100644 src/tests/FunctionalTests/iOS/Simulator/LibraryMode/Program.cs create mode 100644 src/tests/FunctionalTests/iOS/Simulator/LibraryMode/iOS.Simulator.LibraryMode.Test.csproj diff --git a/src/mono/msbuild/apple/build/AppleBuild.targets b/src/mono/msbuild/apple/build/AppleBuild.targets index 44b2f3bdf7895d..99dd2ae72a6dcf 100644 --- a/src/mono/msbuild/apple/build/AppleBuild.targets +++ b/src/mono/msbuild/apple/build/AppleBuild.targets @@ -2,8 +2,7 @@ $(GenerateAppBundle) true - - false + false false @@ -51,16 +50,23 @@ $(AssemblyName) + + marshal-ilgen + + <_CommonLinkerArgs Condition="'$(_IsLibraryMode)' == 'true' and '$(TargetOS)' != 'tvos' and '$(TargetOS)' != 'tvossimulator'" Include="-framework GSS" /> - - marshal-ilgen - - + <_CommonLinkerArgs Include="-lz" /> + <_CommonLinkerArgs Include="-lc++" /> + <_CommonLinkerArgs Include="-liconv" /> + <_CommonLinkerArgs Include="-framework Foundation" /> + <_CommonLinkerArgs Include="-framework Security" /> + <_CommonLinkerArgs Include="-framework UIKit" /> + @@ -236,11 +242,13 @@ + + + + + TargetOS="$(TargetOS)" + UseConsoleUITemplate="$(UseConsoleUITemplate)" + UseNativeAOTRuntime="$(UseNativeAOTRuntime)"> diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs index 5b84eaeac20649..66dbf848d654f8 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs @@ -186,6 +186,11 @@ public string TargetOS /// public string[] NativeDependencies { get; set; } = Array.Empty(); + /// + /// Mode to control whether runtime is a self-contained library or not + /// + public bool IsLibraryMode { get; set; } + public void ValidateRuntimeSelection() { if (UseNativeAOTRuntime) @@ -260,39 +265,38 @@ public override bool Execute() List assemblerFiles = new List(); List assemblerDataFiles = new List(); List assemblerFilesToLink = new List(); - foreach (ITaskItem file in Assemblies) - { - // use AOT files if available - string obj = file.GetMetadata("AssemblerFile"); - string llvmObj = file.GetMetadata("LlvmObjectFile"); - string dataFile = file.GetMetadata("AotDataFile"); - if (!string.IsNullOrEmpty(obj)) + if (!IsLibraryMode) + { + foreach (ITaskItem file in Assemblies) { - assemblerFiles.Add(obj); - } + // use AOT files if available + string obj = file.GetMetadata("AssemblerFile"); + string llvmObj = file.GetMetadata("LlvmObjectFile"); + string dataFile = file.GetMetadata("AotDataFile"); - if (!string.IsNullOrEmpty(dataFile)) - { - assemblerDataFiles.Add(dataFile); + if (!string.IsNullOrEmpty(obj)) + { + assemblerFiles.Add(obj); + } + + if (!string.IsNullOrEmpty(dataFile)) + { + assemblerDataFiles.Add(dataFile); + } + + if (!string.IsNullOrEmpty(llvmObj)) + { + assemblerFilesToLink.Add(llvmObj); + } } - if (!string.IsNullOrEmpty(llvmObj)) + if (!ForceInterpreter && (shouldStaticLink || ForceAOT) && (assemblerFiles.Count == 0 && !UseNativeAOTRuntime)) { - assemblerFilesToLink.Add(llvmObj); + throw new InvalidOperationException("Need list of AOT files for static linked builds."); } } - foreach (var nativeDependency in NativeDependencies) - { - assemblerFilesToLink.Add(nativeDependency); - } - - if (!ForceInterpreter && (shouldStaticLink || ForceAOT) && (assemblerFiles.Count == 0 && !UseNativeAOTRuntime)) - { - throw new InvalidOperationException("Need list of AOT files for static linked builds."); - } - if (!string.IsNullOrEmpty(DiagnosticPorts)) { bool validDiagnosticsConfig = false; @@ -313,6 +317,11 @@ public override bool Execute() throw new ArgumentException("DevTeamProvisioning must be set to a valid value when App Sandbox is enabled, using '-' is not supported."); } + foreach (var nativeDependency in NativeDependencies) + { + assemblerFilesToLink.Add(nativeDependency); + } + List extraLinkerArgs = new List(); foreach(ITaskItem item in ExtraLinkerArguments) { @@ -324,7 +333,7 @@ public override bool Execute() if (GenerateXcodeProject) { XcodeProjectPath = generator.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink, extraLinkerArgs, excludes, - AppDir, binDir, MonoRuntimeHeaders, !shouldStaticLink, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, HybridGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime); + AppDir, binDir, MonoRuntimeHeaders, !shouldStaticLink, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, HybridGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime, IsLibraryMode); if (BuildAppBundle) { @@ -350,7 +359,7 @@ public override bool Execute() else if (GenerateCMakeProject) { generator.GenerateCMake(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink, extraLinkerArgs, excludes, - AppDir, binDir, MonoRuntimeHeaders, !shouldStaticLink, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, HybridGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime); + AppDir, binDir, MonoRuntimeHeaders, !shouldStaticLink, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, HybridGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime, IsLibraryMode); } return true; diff --git a/src/tasks/AppleAppBuilder/Templates/CMakeLists-librarymode.txt.template b/src/tasks/AppleAppBuilder/Templates/CMakeLists-librarymode.txt.template new file mode 100644 index 00000000000000..299bef9c39bfe3 --- /dev/null +++ b/src/tasks/AppleAppBuilder/Templates/CMakeLists-librarymode.txt.template @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 3.16) + +project(%ProjectName%) +enable_language(OBJC ASM) + +set(APP_RESOURCES +%AppResources% +lib%ProjectName%.dylib +) + +add_executable( + %ProjectName% + %MainSource% + ${APP_RESOURCES} +) + +if(NOT %UseNativeAOTRuntime%) + target_sources( + %ProjectName% + PRIVATE + runtime.m) +endif() + +%Defines% + +if(NOT %UseNativeAOTRuntime%) + include_directories("%MonoInclude%") +endif() + +#set_target_properties(%ProjectName% %AotTargetsList% PROPERTIES +# XCODE_ATTRIBUTE_SUPPORTS_MACCATALYST "YES" +#) + +set_target_properties(%ProjectName% PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" + XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING "NO" + XCODE_EMIT_EFFECTIVE_PLATFORM_NAME "YES" + XCODE_EMBED_FRAMEWORKS "%DYLIB_PATH%" + XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" + RESOURCE "${APP_RESOURCES}" +) + +set(HARDENED_RUNTIME +%HardenedRuntime% +) + +set(HARDENED_RUNTIME_USE_ENTITLEMENTS_FILE +%HardenedRuntimeUseEntitlementsFile% +) + +if("${HARDENED_RUNTIME}") + set_target_properties(%ProjectName% PROPERTIES XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES") + if("${HARDENED_RUNTIME_USE_ENTITLEMENTS_FILE}") + set_target_properties(%ProjectName% PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "app.entitlements") + add_custom_command( + TARGET %ProjectName% POST_BUILD + COMMAND if test \"$CODE_SIGN_IDENTITY\"\; then codesign -fs \"$CODE_SIGN_IDENTITY\" $CODESIGNING_FOLDER_PATH/Contents/Resources/*.dylib\; fi + ) + endif() +endif() + +# FIXME: `XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING` should not be NO + +target_link_libraries( + %ProjectName% + PRIVATE + "-framework Foundation" + "-framework Security" + "-framework UIKit" + "-lz" + "-lc++" + "-liconv" + %NativeLibrariesToLink% + %APP_LINK_LIBRARIES% +) + +set_target_properties( + %ProjectName% + PROPERTIES LINK_FLAGS + %EXTRA_LINKER_ARGS% +) diff --git a/src/tasks/AppleAppBuilder/Templates/runtime-librarymode.m b/src/tasks/AppleAppBuilder/Templates/runtime-librarymode.m new file mode 100644 index 00000000000000..b3efb76186f138 --- /dev/null +++ b/src/tasks/AppleAppBuilder/Templates/runtime-librarymode.m @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#import +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#import +#include +#include +#include +#include + +#import "util.h" + +static char *bundle_path; + +int SayHello (void); + +int invoke_netlibrary_entrypoints (void) +{ + return SayHello (); +} + +const char * +get_bundle_path (void) +{ + if (bundle_path) + return bundle_path; + NSBundle* main_bundle = [NSBundle mainBundle]; + NSString* path = [main_bundle bundlePath]; + +#if TARGET_OS_MACCATALYST + path = [path stringByAppendingString:@"/Contents/Resources"]; +#endif + + bundle_path = strdup ([path UTF8String]); + + return bundle_path; +} + +void +mono_ios_runtime_init (void) +{ +#if INVARIANT_GLOBALIZATION + setenv ("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", TRUE); +#endif + +#if HYBRID_GLOBALIZATION + setenv ("DOTNET_SYSTEM_GLOBALIZATION_HYBRID", "1", TRUE); +#endif + +#if ENABLE_RUNTIME_LOGGING + setenv ("MONO_LOG_LEVEL", "debug", TRUE); + setenv ("MONO_LOG_MASK", "all", TRUE); +#endif + + // build using DiagnosticPorts property in AppleAppBuilder + // or set DOTNET_DiagnosticPorts env via mlaunch, xharness when undefined. + // NOTE, using DOTNET_DiagnosticPorts requires app build using AppleAppBuilder and RuntimeComponents=diagnostics_tracing +#ifdef DIAGNOSTIC_PORTS + setenv ("DOTNET_DiagnosticPorts", DIAGNOSTIC_PORTS, true); +#endif + + // When not bundling, this will make sure the runtime can access all the assemblies + const char* bundle = get_bundle_path (); + chdir (bundle); + + int res = invoke_netlibrary_entrypoints (); + + exit (res); +} diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs index dc01dadd30e8f2..d563f14aaaa981 100644 --- a/src/tasks/AppleAppBuilder/Xcode.cs +++ b/src/tasks/AppleAppBuilder/Xcode.cs @@ -190,9 +190,10 @@ public string GenerateXCode( string? diagnosticPorts, string? runtimeComponents = null, string? nativeMainSource = null, - bool useNativeAOTRuntime = false) + bool useNativeAOTRuntime = false, + bool isLibraryMode = false) { - var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmDataFiles, asmLinkFiles, extraLinkerArgs, excludes, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, hybridGlobalization, optimized, enableRuntimeLogging, enableAppSandbox, diagnosticPorts, runtimeComponents, nativeMainSource, useNativeAOTRuntime); + var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmDataFiles, asmLinkFiles, extraLinkerArgs, excludes, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, hybridGlobalization, optimized, enableRuntimeLogging, enableAppSandbox, diagnosticPorts, runtimeComponents, nativeMainSource, useNativeAOTRuntime, isLibraryMode); CreateXcodeProject(projectName, cmakeDirectoryPath); return Path.Combine(binDir, projectName, projectName + ".xcodeproj"); } @@ -253,7 +254,8 @@ public string GenerateCMake( string? diagnosticPorts, string? runtimeComponents = null, string? nativeMainSource = null, - bool useNativeAOTRuntime = false) + bool useNativeAOTRuntime = false, + bool isLibraryMode = false) { // bundle everything as resources excluding native files var predefinedExcludes = new List { ".dll.o", ".dll.s", ".dwarf", ".m", ".h", ".a", ".bc", "libmonosgen-2.0.dylib", "libcoreclr.dylib", "icudt*" }; @@ -313,7 +315,8 @@ public string GenerateCMake( string appResources = string.Join(Environment.NewLine, asmDataFiles.Select(r => " " + r)); appResources += string.Join(Environment.NewLine, resources.Where(r => !r.EndsWith("-llvm.o")).Select(r => " " + Path.GetRelativePath(binDir, r))); - string cmakeLists = Utils.GetEmbeddedResource("CMakeLists.txt.template") + string cmakeTemplateName = (isLibraryMode) ? "CMakeLists-librarymode.txt.template" : "CMakeLists.txt.template"; + string cmakeLists = Utils.GetEmbeddedResource(cmakeTemplateName) .Replace("%UseNativeAOTRuntime%", useNativeAOTRuntime ? "TRUE" : "FALSE") .Replace("%ProjectName%", projectName) .Replace("%AppResources%", appResources) @@ -322,85 +325,93 @@ public string GenerateCMake( .Replace("%HardenedRuntime%", hardenedRuntime ? "TRUE" : "FALSE"); string toLink = ""; + string aotSources = ""; + string aotList = ""; - string[] allComponentLibs = Directory.GetFiles(workspace, "libmono-component-*-static.a"); - string[] staticComponentStubLibs = Directory.GetFiles(workspace, "libmono-component-*-stub-static.a"); - bool staticLinkAllComponents = false; - string[] staticLinkedComponents = Array.Empty(); + if (isLibraryMode) + { + string dylibName = $"lib{projectName}.dylib"; + cmakeLists = cmakeLists.Replace("%DYLIB_PATH%", $"{Path.Combine(binDir, dylibName)}"); + } + else + { + string[] allComponentLibs = Directory.GetFiles(workspace, "libmono-component-*-static.a"); + string[] staticComponentStubLibs = Directory.GetFiles(workspace, "libmono-component-*-stub-static.a"); + bool staticLinkAllComponents = false; + string[] staticLinkedComponents = Array.Empty(); - if (!string.IsNullOrEmpty(runtimeComponents) && runtimeComponents.Equals("*", StringComparison.OrdinalIgnoreCase)) - staticLinkAllComponents = true; - else if (!string.IsNullOrEmpty(runtimeComponents)) - staticLinkedComponents = runtimeComponents.Split(";"); + if (!string.IsNullOrEmpty(runtimeComponents) && runtimeComponents.Equals("*", StringComparison.OrdinalIgnoreCase)) + staticLinkAllComponents = true; + else if (!string.IsNullOrEmpty(runtimeComponents)) + staticLinkedComponents = runtimeComponents.Split(";"); - // by default, component stubs will be linked and depending on how mono runtime has been build, - // stubs can disable or dynamic load components. - foreach (string staticComponentStubLib in staticComponentStubLibs) - { - string componentLibToLink = staticComponentStubLib; - if (staticLinkAllComponents) - { - // static link component. - componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase); - } - else + // by default, component stubs will be linked and depending on how mono runtime has been build, + // stubs can disable or dynamic load components. + foreach (string staticComponentStubLib in staticComponentStubLibs) { - foreach (string staticLinkedComponent in staticLinkedComponents) + string componentLibToLink = staticComponentStubLib; + if (staticLinkAllComponents) + { + // static link component. + componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase); + } + else { - if (componentLibToLink.Contains(staticLinkedComponent, StringComparison.OrdinalIgnoreCase)) + foreach (string staticLinkedComponent in staticLinkedComponents) { - // static link component. - componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase); - break; + if (componentLibToLink.Contains(staticLinkedComponent, StringComparison.OrdinalIgnoreCase)) + { + // static link component. + componentLibToLink = componentLibToLink.Replace("-stub-static.a", "-static.a", StringComparison.OrdinalIgnoreCase); + break; + } } } - } - // if lib doesn't exist (primarily due to runtime build without static lib support), fallback linking stub lib. - if (!File.Exists(componentLibToLink)) - { - Logger.LogMessage(MessageImportance.High, $"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n"); - componentLibToLink = staticComponentStubLib; - } + // if lib doesn't exist (primarily due to runtime build without static lib support), fallback linking stub lib. + if (!File.Exists(componentLibToLink)) + { + Logger.LogMessage(MessageImportance.High, $"\nCouldn't find static component library: {componentLibToLink}, linking static component stub library: {staticComponentStubLib}.\n"); + componentLibToLink = staticComponentStubLib; + } - toLink += $" \"-force_load {componentLibToLink}\"{Environment.NewLine}"; - } + toLink += $" \"-force_load {componentLibToLink}\"{Environment.NewLine}"; + } - string[] dylibs = Directory.GetFiles(workspace, "*.dylib"); - foreach (string lib in Directory.GetFiles(workspace, "*.a")) - { - // all component libs already added to linker. - if (allComponentLibs.Any(lib.Contains)) - continue; + string[] dylibs = Directory.GetFiles(workspace, "*.dylib"); + foreach (string lib in Directory.GetFiles(workspace, "*.a")) + { + // all component libs already added to linker. + if (allComponentLibs.Any(lib.Contains)) + continue; - string libName = Path.GetFileNameWithoutExtension(lib); - // libmono must always be statically linked, for other librarires we can use dylibs - bool dylibExists = libName != "libmonosgen-2.0" && dylibs.Any(dylib => Path.GetFileName(dylib) == libName + ".dylib"); + string libName = Path.GetFileNameWithoutExtension(lib); + // libmono must always be statically linked, for other librarires we can use dylibs + bool dylibExists = libName != "libmonosgen-2.0" && dylibs.Any(dylib => Path.GetFileName(dylib) == libName + ".dylib"); - if (useNativeAOTRuntime) - { - // link NativeAOT framework libs without '-force_load' - toLink += $" {lib}{Environment.NewLine}"; + if (useNativeAOTRuntime) + { + // link NativeAOT framework libs without '-force_load' + toLink += $" {lib}{Environment.NewLine}"; + } + else if (forceAOT || !(preferDylibs && dylibExists)) + { + // these libraries are pinvoked + // -force_load will be removed once we enable direct-pinvokes for AOT + toLink += $" \"-force_load {lib}\"{Environment.NewLine}"; + } } - else if (forceAOT || !(preferDylibs && dylibExists)) + + foreach (string asm in asmFiles) { - // these libraries are pinvoked - // -force_load will be removed once we enable direct-pinvokes for AOT - toLink += $" \"-force_load {lib}\"{Environment.NewLine}"; + // these libraries are linked via modules.m + var name = Path.GetFileNameWithoutExtension(asm); + aotSources += $"add_library({projectName}_{name} OBJECT {asm}){Environment.NewLine}"; + toLink += $" {projectName}_{name}{Environment.NewLine}"; + aotList += $" {projectName}_{name}"; } } - string aotSources = ""; - string aotList = ""; - foreach (string asm in asmFiles) - { - // these libraries are linked via modules.m - var name = Path.GetFileNameWithoutExtension(asm); - aotSources += $"add_library({projectName}_{name} OBJECT {asm}){Environment.NewLine}"; - toLink += $" {projectName}_{name}{Environment.NewLine}"; - aotList += $" {projectName}_{name}"; - } - foreach (string asmLinkFile in asmLinkFiles) { toLink += $" {asmLinkFile}{Environment.NewLine}"; @@ -500,8 +511,9 @@ public string GenerateCMake( dllMap.AppendLine($" mono_dllmap_insert (NULL, \"System.Globalization.Native\", NULL, \"__Internal\", NULL);"); + string runtimeTemplateName = (isLibraryMode) ? "runtime-librarymode.m" : "runtime.m"; File.WriteAllText(Path.Combine(binDir, "runtime.m"), - Utils.GetEmbeddedResource("runtime.m") + Utils.GetEmbeddedResource(runtimeTemplateName) .Replace("//%DllMap%", dllMap.ToString()) .Replace("//%APPLE_RUNTIME_IDENTIFIER%", RuntimeIdentifier) .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib))); diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.cs b/src/tasks/LibraryBuilder/LibraryBuilder.cs index b32278694a654c..537194479fc3ba 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.cs +++ b/src/tasks/LibraryBuilder/LibraryBuilder.cs @@ -10,6 +10,8 @@ using System.Linq; using System.Runtime.InteropServices; using Microsoft.Android.Build; +using Microsoft.Apple.Build; +using Microsoft.Mobile.Build.Clang; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -18,7 +20,6 @@ public class LibraryBuilderTask : AppBuilderTask private bool isSharedLibrary = true; private string nativeLibraryType = "SHARED"; - private string cmakeProjectLanguages = ""; private string targetOS = ""; private bool usesAOTDataFile; private List exportedAssemblies = new List(); @@ -118,25 +119,11 @@ private string MobileSymbolFileName get => Path.Combine(OutputDirectory, "mobile_symbols.txt"); } - private string CMakeProjectLanguages - { - get - { - if (string.IsNullOrEmpty(cmakeProjectLanguages)) - { - cmakeProjectLanguages = (TargetOS == "android") ? "C ASM" : "OBJC ASM"; - } - - return cmakeProjectLanguages; - } - } - public override bool Execute() { - StringBuilder aotSources = new StringBuilder(); - StringBuilder aotObjects = new StringBuilder(); - StringBuilder extraSources = new StringBuilder(); - StringBuilder linkerArgs = new StringBuilder(); + List sources = new List(); + List libs = new List(); + List linkerArgs = new List(); if (!ValidateValidTargetOS()) { @@ -149,44 +136,25 @@ public override bool Execute() return false; } - if (TargetOS == "android") - { - List sources = new List(); - List libs = new List(); - List lArgs = new List(); - - GatherSourcesAndLibs(sources, libs, lArgs); + GatherSourcesAndLibs(sources, libs, linkerArgs); - File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), - Utils.GetEmbeddedResource("library-builder.h")); + File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), + Utils.GetEmbeddedResource("library-builder.h")); - GenerateAssembliesLoader(); + GenerateAssembliesLoader(); - if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) - { - WriteAutoInitializationFromTemplate(); - } + if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) + { + WriteAutoInitializationFromTemplate(); + } - OutputPath = BuildLibrary(sources, libs, lArgs); + if (TargetOS == "android") + { + OutputPath = BuildAndroidLibrary(sources, libs, linkerArgs); } else { - GatherAotSourcesObjects(aotSources, aotObjects, extraSources, linkerArgs); - GatherLinkerArgs(linkerArgs); - - File.WriteAllText(Path.Combine(OutputDirectory, "library-builder.h"), - Utils.GetEmbeddedResource("library-builder.h")); - - GenerateAssembliesLoader(); - - if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) - { - WriteAutoInitializationFromTemplate(); - extraSources.AppendLine(" autoinit.c"); - } - - WriteCMakeFileFromTemplate(aotSources.ToString(), aotObjects.ToString(), extraSources.ToString(), linkerArgs.ToString()); - OutputPath = BuildLibrary(); + OutputPath = BuildAppleLibrary(sources, libs, linkerArgs); } return true; @@ -204,6 +172,11 @@ private void GatherSourcesAndLibs(List sources, List libs, List< usesAOTDataFile = true; } + if (!string.IsNullOrEmpty(compiledAssembly.AssemblerFile)) + { + sources.Add(compiledAssembly.AssemblerFile); + } + if (!string.IsNullOrEmpty(compiledAssembly.LlvmObjectFile)) { sources.Add(compiledAssembly.LlvmObjectFile); @@ -254,88 +227,29 @@ private void GatherSourcesAndLibs(List sources, List libs, List< // for android, all symbols to keep go in one linker script // // for ios, multiple files can be specified - WriteLinkerScriptFile(MobileSymbolFileName, exportedSymbols); - linkerArgs.Add($"\"--version-script={MobileSymbolFileName}\""); - } - - foreach (ITaskItem item in ExtraSources) - { - sources.Add(item.ItemSpec); - } - - ExportedSymbols = exportedSymbols.ToArray(); - } - - // intended for cmake based builds - private void GatherAotSourcesObjects(StringBuilder aotSources, StringBuilder aotObjects, StringBuilder extraSources, StringBuilder linkerArgs) - { - List exportedSymbols = new List(); - - foreach (CompiledAssembly compiledAssembly in CompiledAssemblies) - { - if (!string.IsNullOrEmpty(compiledAssembly.AssemblerFile)) - { - aotSources.AppendLine($" {compiledAssembly.AssemblerFile}"); - } - - if (!usesAOTDataFile && !string.IsNullOrEmpty(compiledAssembly.DataFile)) - { - usesAOTDataFile = true; - } - - if (!string.IsNullOrEmpty(compiledAssembly.LlvmObjectFile)) + if (TargetOS == "android") { - aotObjects.AppendLine($" {compiledAssembly.LlvmObjectFile}"); + WriteLinkerScriptFile(MobileSymbolFileName, exportedSymbols); + linkerArgs.Add($"\"--version-script={MobileSymbolFileName}\""); } - - if (!string.IsNullOrEmpty(compiledAssembly.ExportsFile)) + else { - int symbolsAdded = GatherExportedSymbols(compiledAssembly.ExportsFile, exportedSymbols); - - if (symbolsAdded > 0) - { - exportedAssemblies.Add(Path.GetFileName(compiledAssembly.Path)); - } + File.WriteAllText( + MobileSymbolFileName, + string.Join("\n", exportedSymbols.Select(symbol => symbol)) + ); + linkerArgs.Add($"-exported_symbols_list {MobileSymbolFileName}"); } } - if (exportedAssemblies.Count == 0) - { - throw new LogAsErrorException($"None of the compiled assemblies contain exported symbols. The library must export only symbols resulting from [UnmanageCallersOnly(Entrypoint = )]Resulting shared library would be unusable."); - } - - if (IsSharedLibrary) - { - File.WriteAllText( - MobileSymbolFileName, - string.Join("\n", exportedSymbols.Select(symbol => symbol)) - ); - WriteExportedSymbolsArg(MobileSymbolFileName, linkerArgs); - } - foreach (ITaskItem item in ExtraSources) { - extraSources.AppendLine($" {item.ItemSpec}"); + sources.Add(item.ItemSpec); } ExportedSymbols = exportedSymbols.ToArray(); } - private void GatherLinkerArgs(StringBuilder linkerArgs) - { - string libForceLoad = "-force_load "; - - foreach (ITaskItem item in RuntimeLibraries) - { - linkerArgs.AppendLine($" \"{libForceLoad}{item.ItemSpec}\""); - } - - foreach (ITaskItem item in ExtraLinkerArguments) - { - linkerArgs.AppendLine($" \"{item.ItemSpec}\""); - } - } - private static int GatherExportedSymbols(string exportsFile, List exportedSymbols) { int count = 0; @@ -349,16 +263,6 @@ private static int GatherExportedSymbols(string exportsFile, List export return count; } - private static void WriteExportedSymbolsArg(string exportsFile, StringBuilder linkerArgs) - { - linkerArgs.AppendLine($" \"-Wl,-exported_symbols_list {exportsFile}\""); - } - - private static void WriteLinkerScriptArg(string exportsFile, StringBuilder linkerArgs) - { - linkerArgs.AppendLine($" \"-Wl,--version-script={exportsFile}\""); - } - private static void WriteLinkerScriptFile(string exportsFile, List exportedSymbols) { string globalExports = string.Join(";\n", exportedSymbols.Select(symbol => symbol)); @@ -423,46 +327,11 @@ private void GenerateAssembliesLoader() .Replace("%ASSEMBLIES_PRELOADER%", string.Join("\n ", assemblyPreloaders))); } - private void WriteCMakeFileFromTemplate(string aotSources, string aotObjects, string extraSources, string linkerArgs) - { - string extraDefinitions = GenerateExtraDefinitions(); - // BundleDir - File.WriteAllText(Path.Combine(OutputDirectory, "CMakeLists.txt"), - Utils.GetEmbeddedResource("CMakeLists.txt.template") - .Replace("%LIBRARY_NAME%", Name) - .Replace("%LIBRARY_TYPE%", nativeLibraryType) - .Replace("%CMAKE_LANGS%", CMakeProjectLanguages) - .Replace("%MonoInclude%", MonoRuntimeHeaders) - .Replace("%AotSources%", aotSources) - .Replace("%AotObjects%", aotObjects) - .Replace("%ExtraDefinitions%", extraDefinitions) - .Replace("%ExtraSources%", extraSources) - .Replace("%LIBRARY_LINKER_ARGS%", linkerArgs)); - } - - private string GenerateExtraDefinitions() - { - var extraDefinitions = new StringBuilder(); - - if (usesAOTDataFile) - { - extraDefinitions.AppendLine("add_definitions(-DUSES_AOT_DATA=1)"); - } - - if (BundlesResources) - { - extraDefinitions.AppendLine("add_definitions(-DBUNDLED_RESOURCES=1)"); - } - - return extraDefinitions.ToString(); - } - - // For now Android only using the NDK toolchain - private string BuildLibrary(List sources, List libs, List linkerArgs) + private string BuildAndroidLibrary(List sources, List libs, List linkerArgs) { string libraryName = GetLibraryName(); - AndroidBuildOptions buildOptions = new AndroidBuildOptions(); + ClangBuildOptions buildOptions = new ClangBuildOptions(); buildOptions.CompilerArguments.Add("-D ANDROID=1"); buildOptions.CompilerArguments.Add("-D HOST_ANDROID=1"); buildOptions.CompilerArguments.Add("-fPIC"); @@ -495,17 +364,51 @@ private string BuildLibrary(List sources, List libs, List sources, List libs, List linkerArgs) { - string libraryOutputPath; - Xcode project = new Xcode(Log, RuntimeIdentifier); - project.CreateXcodeProject("netlibrary", OutputDirectory); + string libraryName = GetLibraryName(); + + ClangBuildOptions buildOptions = new ClangBuildOptions(); + buildOptions.CompilerArguments.Add(IsSharedLibrary ? $"-dynamiclib -o {libraryName}" : $"-o {libraryName}"); + buildOptions.CompilerArguments.Add("-D HOST_APPLE_MOBILE=1"); + buildOptions.CompilerArguments.Add("-D FORCE_AOT=1"); + buildOptions.IncludePaths.Add(MonoRuntimeHeaders); + buildOptions.NativeLibraryPaths.AddRange(libs); + buildOptions.Sources.AddRange(sources); + buildOptions.Sources.Add("preloaded-assemblies.c"); + + if (IsSharedLibrary) + { + linkerArgs.Add("-Wl,-headerpad_max_install_names"); + } + + buildOptions.LinkerArguments.AddRange(linkerArgs); + + if (UsesRuntimeInitCallback && !UsesCustomRuntimeInitCallback) + { + buildOptions.Sources.Add("autoinit.c"); + } - string xcodeProjectPath = Path.Combine(OutputDirectory, "netlibrary", $"{Name}.xcodeproj"); - libraryOutputPath = project.BuildAppBundle(xcodeProjectPath, StripDebugSymbols, "-"); + if (BundlesResources) + { + buildOptions.CompilerArguments.Add("-D BUNDLED_RESOURCES=1"); + } - return Path.Combine(libraryOutputPath, GetLibraryName()); + if (usesAOTDataFile) + { + buildOptions.CompilerArguments.Add("-D USES_AOT_DATA=1"); + } + + AppleProject project = new AppleProject("netlibrary", RuntimeIdentifier, Log); + project.Build(OutputDirectory, buildOptions, StripDebugSymbols); + + if (IsSharedLibrary) + { + string installToolArgs = $"install_name_tool -id @rpath/{libraryName} {libraryName}"; + Utils.RunProcess(Log, "xcrun", workingDir: OutputDirectory, args: installToolArgs); + } + + return Path.Combine(OutputDirectory, libraryName); } private string GetLibraryName() diff --git a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs index eb41d452333395..4072c4207e10a1 100644 --- a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs +++ b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs @@ -6,6 +6,7 @@ using System.IO; using System.Text; using Microsoft.Android.Build.Ndk; +using Microsoft.Mobile.Build.Clang; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -41,7 +42,7 @@ public AndroidProject(string projectName, string runtimeIdentifier, string andro } // builds using NDK toolchain - public void Build(string workingDir, AndroidBuildOptions buildOptions, bool stripDebugSymbols = false, string apiLevel = DefaultMinApiLevel) + public void Build(string workingDir, ClangBuildOptions buildOptions, bool stripDebugSymbols = false, string apiLevel = DefaultMinApiLevel) { NdkTools tools = new NdkTools(targetArchitecture, GetHostOS(), apiLevel); @@ -90,7 +91,7 @@ public string BuildCMake(string workingDir, bool stripDebugSymbols = false) return Path.Combine(workingDir, projectName); } - private static string BuildClangArgs(AndroidBuildOptions buildOptions) + private static string BuildClangArgs(ClangBuildOptions buildOptions) { StringBuilder ret = new StringBuilder(); diff --git a/src/tasks/MobileBuildTasks/Apple/AppleProject.cs b/src/tasks/MobileBuildTasks/Apple/AppleProject.cs new file mode 100644 index 00000000000000..ed3c2c56c197b2 --- /dev/null +++ b/src/tasks/MobileBuildTasks/Apple/AppleProject.cs @@ -0,0 +1,162 @@ +// 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.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Mobile.Build.Clang; + +namespace Microsoft.Apple.Build +{ + public sealed class AppleProject + { + private const string DefaultMinOSVersion = "11.0"; + + private TaskLoggingHelper logger; + + private string projectName; + private string sdkRoot; + private string targetAbi; + private string targetArchitecture; + private string targetOS; + + public AppleProject(string projectName, string runtimeIdentifier, TaskLoggingHelper logger) + { + GetTargets(runtimeIdentifier, out targetOS, out targetArchitecture); + targetAbi = DetermineAbi(targetArchitecture); + + AppleSdk sdk = new AppleSdk(targetOS, logger); + sdkRoot = sdk.SdkRoot; + + this.logger = logger; + this.projectName = projectName; + } + + public string SdkRoot + { + get => sdkRoot; + set + { + sdkRoot = value; + } + } + + public void Build(string workingDir, ClangBuildOptions buildOptions, bool stripDebugSymbols = false, string minOSVersion = DefaultMinOSVersion) + { + string clangArgs = BuildClangArgs(buildOptions, minOSVersion); + Utils.RunProcess(logger, "xcrun", workingDir: workingDir, args: clangArgs); + } + + private string BuildClangArgs(ClangBuildOptions buildOptions, string minOSVersion) + { + StringBuilder ret = new StringBuilder(); + + ret.Append("clang "); + + if (targetOS == "maccatalyst") + { + string frameworkPath = Path.Combine(SdkRoot, "System", "iOSSupport", "System", "Library", "Frameworks"); + string iosLibPath = Path.Combine(SdkRoot, "System", "iOSSupport", "usr", "lib"); + + buildOptions.CompilerArguments.Add($"-target {targetAbi}-apple-ios{minOSVersion}-macabi"); + buildOptions.CompilerArguments.Add($"-isysroot {SdkRoot}"); + buildOptions.CompilerArguments.Add($"-iframework {frameworkPath}"); + + ret.Append($"-L {iosLibPath}"); + ret.Append(' '); + } + else + { + buildOptions.CompilerArguments.Add($"-m{targetOS}-version-min={minOSVersion}"); + buildOptions.CompilerArguments.Add($"-isysroot {sdkRoot}"); + buildOptions.CompilerArguments.Add($"-arch {targetAbi}"); + } + + foreach(string compilerArg in buildOptions.CompilerArguments) + { + ret.Append(compilerArg); + ret.Append(' '); + } + + foreach(string includeDir in buildOptions.IncludePaths) + { + ret.Append($"-I {includeDir} "); + } + + foreach(string linkerArg in buildOptions.LinkerArguments) + { + ret.Append($"{linkerArg} "); + } + + foreach(string source in buildOptions.Sources) + { + string ext = Path.GetExtension(source); + + if (ext == ".a") + { + ret.Append($"-force_load {source}"); + } + else + { + ret.Append(source); + } + + ret.Append(' '); + } + + HashSet libDirs = new HashSet(); + foreach(string lib in buildOptions.NativeLibraryPaths) + { + string rootPath = Path.GetDirectoryName(lib)!; + string libName = Path.GetFileName(lib); + string ext = Path.GetExtension(lib); + + if (ext == ".a") + { + ret.Append($"-force_load {lib}"); + ret.Append(' '); + } + else + { + if (!libDirs.Contains(rootPath)) + { + libDirs.Add(rootPath); + ret.Append($"-L {rootPath} "); + } + ret.Append($"-l{libName} "); + } + } + + return ret.ToString(); + } + + private static void GetTargets(string runtimeIdentifier, out string os, out string arch) + { + int pos = runtimeIdentifier.IndexOf('-'); + os = (pos > -1) ? DetermineTargetOS(runtimeIdentifier.Substring(0, pos)) : ""; + arch = (pos > -1) ? runtimeIdentifier.Substring(pos + 1) : ""; + } + + private static string DetermineAbi(string arch) => + arch switch + { + "arm64" => "arm64", + "x64" => "x86_64", + _ => throw new ArgumentException($"{arch} is not supported"), + }; + + private static string DetermineTargetOS(string os) => + os switch + { + "ios" => "ios", + "iossimulator" => "iphonesimulator", + "tvos" => "tvos", + "tvossimulator" => "tvos-simulator", + "maccatalyst" => "maccatalyst", + _ => throw new ArgumentException($"{os} is not supported") + }; + } +} diff --git a/src/tasks/MobileBuildTasks/Apple/AppleSdk.cs b/src/tasks/MobileBuildTasks/Apple/AppleSdk.cs new file mode 100644 index 00000000000000..c71f4b942c0e4a --- /dev/null +++ b/src/tasks/MobileBuildTasks/Apple/AppleSdk.cs @@ -0,0 +1,143 @@ +// 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; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.Apple.Build +{ + public sealed class AppleSdk + { + private string devRoot; + private string platformName; + private string sdkDir; + private string sdkRoot; + + private TaskLoggingHelper logger; + + public AppleSdk(string targetOS, TaskLoggingHelper logger) + { + this.logger = logger; + + platformName = GetPlatformName(targetOS); + devRoot = GetXCodeDevRoot(); + + sdkDir = Path.Combine(devRoot, "Contents", "Developer", "Platforms", $"{platformName}.platform", "Developer", "SDKs"); + sdkRoot = GetSdkRoot(sdkDir, platformName); + } + + public string DeveloperRoot + { + get => devRoot; + } + + public string SdkRoot + { + get => sdkRoot; + } + + private static string GetSdkRoot(string sdkDir, string platformName) + { + string sdkRoot = ""; + + if (!Directory.Exists(sdkDir)) + { + return sdkRoot; + } + + List sdks = new List(); + + foreach (var dir in Directory.GetDirectories(sdkDir)) + { + if (!File.Exists(Path.Combine(dir, "SDKSettings.plist"))) + { + continue; + } + + string d = Path.GetFileName(dir); + if (!d.StartsWith(platformName, StringComparison.Ordinal)) + { + continue; + } + + d = d.Substring(platformName.Length); + if (!d.EndsWith(".sdk", StringComparison.Ordinal)) + { + continue; + } + + d = d.Substring(0, d.Length - ".sdk".Length); + if (d.Length > 0) + { + sdks.Add(d); + } + } + + if (sdks.Count > 0) + { + string version = sdks[sdks.Count - 1]; + sdkRoot = Path.Combine(sdkDir, $"{platformName}{version}.sdk"); + } + + return sdkRoot; + } + + private static string GetXCodeDevRoot() + { + string path = ""; + + if (!File.Exists ("/usr/bin/xcode-select")) + { + return path; + } + + try + { + Process process = new Process (); + process.StartInfo.FileName = "/usr/bin/xcode-select"; + process.StartInfo.Arguments = "--print-path"; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.UseShellExecute = false; + process.Start(); + + string stdout = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + stdout = stdout.Trim(); + if (Directory.Exists(stdout)) + { + if (stdout.EndsWith("/Contents/Developer", StringComparison.Ordinal)) + { + stdout = stdout.Substring(0, stdout.Length - "/Contents/Developer".Length); + } + + path = stdout; + } + } + catch (Exception e) + { + throw new Exception("Could not get installed xcode location", e); + } + + if (string.IsNullOrEmpty(path)) + { + throw new Exception("Could not get installed xcode location"); + } + + return path; + } + + public static string GetPlatformName(string targetOS) => + targetOS switch + { + "ios" => "iPhoneOS", + "iphonesimulator" => "iPhoneSimulator", + "tvos" => "AppleTVOS", + "tvos-simulator" => "AppleTVSimulator", + _ => throw new ArgumentException($"{targetOS} does not have a valid platform name") + }; + } +} diff --git a/src/tasks/MobileBuildTasks/Android/AndroidBuildOptions.cs b/src/tasks/MobileBuildTasks/Clang/ClangBuildOptions.cs similarity index 89% rename from src/tasks/MobileBuildTasks/Android/AndroidBuildOptions.cs rename to src/tasks/MobileBuildTasks/Clang/ClangBuildOptions.cs index 3893dd4de06a2b..8f7bd5fa87b5f4 100644 --- a/src/tasks/MobileBuildTasks/Android/AndroidBuildOptions.cs +++ b/src/tasks/MobileBuildTasks/Clang/ClangBuildOptions.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; -namespace Microsoft.Android.Build +namespace Microsoft.Mobile.Build.Clang { - public sealed class AndroidBuildOptions + public sealed class ClangBuildOptions { public List CompilerArguments { get; } = new List(); diff --git a/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/ILLink.Descriptors.xml b/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/ILLink.Descriptors.xml new file mode 100644 index 00000000000000..407eecfd7968c2 --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/ILLink.Descriptors.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/Program.cs new file mode 100644 index 00000000000000..75b4fa4f6d7515 --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/Program.cs @@ -0,0 +1,29 @@ +// 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.Threading; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +public static class Program +{ + [DllImport("__Internal")] + public static extern void mono_ios_set_summary (string value); + + [UnmanagedCallersOnly(EntryPoint = nameof(SayHello))] + public static int SayHello() + { + Console.WriteLine("Called from native! Hello!"); + return 42; + } + + public static async Task Main(string[] args) + { + mono_ios_set_summary($"Starting functional test"); + Console.WriteLine("Done!"); + await Task.Delay(5000); + + return 42; + } +} diff --git a/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/iOS.Simulator.LibraryMode.Test.csproj b/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/iOS.Simulator.LibraryMode.Test.csproj new file mode 100644 index 00000000000000..0454742254a2ca --- /dev/null +++ b/src/tests/FunctionalTests/iOS/Simulator/LibraryMode/iOS.Simulator.LibraryMode.Test.csproj @@ -0,0 +1,29 @@ + + + + Exe + false + true + true + $(NetCoreAppCurrent) + iossimulator + iOS.Simulator.LibraryMode.Test.dll + false + 42 + true + true + true + shared + false + false + + + + + + + + + + + From 450d8911c959cf2c2b95a79217b3503823e79df9 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 2 Aug 2023 15:07:22 -0400 Subject: [PATCH 10/12] Revert android fun test addition --- ...id.Device_Emulator.LibraryMode.Test.csproj | 25 ------------------- .../LibraryMode/ILLink.Descriptors.xml | 7 ------ .../Device_Emulator/LibraryMode/Program.cs | 20 --------------- 3 files changed, 52 deletions(-) delete mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj delete mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/ILLink.Descriptors.xml delete mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj deleted file mode 100644 index da11e1ea6116af..00000000000000 --- a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Android.Device_Emulator.LibraryMode.Test.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - Exe - true - false - true - true - shared - true - $(NetCoreAppCurrent) - Android.Device_Emulator.LibraryMode.Test.dll - 42 - true - true - - - - - - - - - - - diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/ILLink.Descriptors.xml b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/ILLink.Descriptors.xml deleted file mode 100644 index 7fbe2a5cde589f..00000000000000 --- a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/ILLink.Descriptors.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs deleted file mode 100644 index 916d1f1aa03b80..00000000000000 --- a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode/Program.cs +++ /dev/null @@ -1,20 +0,0 @@ -// 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.Runtime.InteropServices; - -public static class Program -{ - [UnmanagedCallersOnly(EntryPoint = nameof(SayHello))] - public static void SayHello() - { - Console.WriteLine("Called from native! Hello!"); - } - - public static int Main() - { - Console.WriteLine("Hello, Android!"); // logcat - return 42; - } -} From 0e21af6a6ed62b037b8329588752c1887d5a0723 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 2 Aug 2023 19:30:45 -0400 Subject: [PATCH 11/12] Fix missing prototype error on the aot functional tests --- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 3 ++- src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index a7c8518317d1f3..9d12e0805d8a3f 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -1106,7 +1106,8 @@ private bool GenerateAotModulesTable(IEnumerable assemblies, string[] { writer.WriteLine($"extern void *{symbol};"); } - writer.WriteLine("void register_aot_modules ()"); + writer.WriteLine("void register_aot_modules (void);"); + writer.WriteLine("void register_aot_modules (void)"); writer.WriteLine("{"); foreach (var symbol in symbols) { diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs index 42fc0c24e9a1bf..6370c49df85405 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs @@ -31,7 +31,7 @@ public sealed class NdkTools private static readonly Dictionary validArches = new Dictionary() { - { "arm", new AndroidArch("arm", "armeabi-v7a", "armv7a-linux-androideabi") }, + { "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") } From 41a6af12bb5ad8862c5ecc58d71cd6d557bc84b1 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Fri, 4 Aug 2023 16:30:42 -0400 Subject: [PATCH 12/12] Feedback --- Directory.Build.props | 1 + src/tasks/MobileBuildTasks/Apple/AppleSdk.cs | 44 ++++++++++---------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 384d2deb85f770..f6776738457046 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -57,6 +57,7 @@ - src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets - src/installer/pkg/sfx/bundle/shared-framework-distribution-template-x64.xml - src/installer/pkg/sfx/bundle/shared-framework-distribution-template-arm64.xml + - src/tasks/MobileBuildTasks/Apple/AppleProject.cs --> 21 11.0 diff --git a/src/tasks/MobileBuildTasks/Apple/AppleSdk.cs b/src/tasks/MobileBuildTasks/Apple/AppleSdk.cs index c71f4b942c0e4a..203ff8fa3a898f 100644 --- a/src/tasks/MobileBuildTasks/Apple/AppleSdk.cs +++ b/src/tasks/MobileBuildTasks/Apple/AppleSdk.cs @@ -85,46 +85,44 @@ private static string GetSdkRoot(string sdkDir, string platformName) return sdkRoot; } - private static string GetXCodeDevRoot() + private string GetXCodeDevRoot() { string path = ""; + string output; if (!File.Exists ("/usr/bin/xcode-select")) { - return path; + throw new Exception("Unable to locate XCode via xcode-select. Please make sure Xcode is properly installed"); } try { - Process process = new Process (); - process.StartInfo.FileName = "/usr/bin/xcode-select"; - process.StartInfo.Arguments = "--print-path"; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.UseShellExecute = false; - process.Start(); - - string stdout = process.StandardOutput.ReadToEnd(); - process.WaitForExit(); - - stdout = stdout.Trim(); - if (Directory.Exists(stdout)) + (int exitCode, output) = Utils.TryRunProcess(logger, + "/usr/bin/xcode-select", + "--print-path", + silent: true, + debugMessageImportance: MessageImportance.Low, + label: "xcode-select"); + + output.Trim(); + if (Directory.Exists(output)) { - if (stdout.EndsWith("/Contents/Developer", StringComparison.Ordinal)) + if (output.EndsWith("/Contents/Developer", StringComparison.Ordinal)) { - stdout = stdout.Substring(0, stdout.Length - "/Contents/Developer".Length); + output = output.Substring(0, output.Length - "/Contents/Developer".Length); } - path = stdout; + path = output; + + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException("Could not find the path to Xcode via xcode-select. Please make sure Xcode is properly installed."); + } } } catch (Exception e) { - throw new Exception("Could not get installed xcode location", e); - } - - if (string.IsNullOrEmpty(path)) - { - throw new Exception("Could not get installed xcode location"); + throw new Exception("Could not get installed Xcode location", e); } return path;