From 8642a210c178f1608e4b9a075987d3dc2b734ffa Mon Sep 17 00:00:00 2001 From: Ivan Povazan <55002338+ivanpovazan@users.noreply.github.com> Date: Tue, 7 Feb 2023 19:14:50 +0100 Subject: [PATCH] Enable AppleAppBuilder to bundle iOS applications built for NativeAOT runtime (#81711) Fixes: https://github.com/dotnet/runtime/issues/80910 --- src/tasks/AppleAppBuilder/AppleAppBuilder.cs | 49 +++++++++++++++-- .../Templates/CMakeLists.txt.template | 16 ++++-- src/tasks/AppleAppBuilder/Templates/main-simple.m | 13 +++++ src/tasks/AppleAppBuilder/Xcode.cs | 64 ++++++++++++++-------- 4 files changed, 110 insertions(+), 32 deletions(-) diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs index 859db0b..6182f97 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs @@ -46,7 +46,6 @@ public class AppleAppBuilderTask : Task /// /// Path to Mono public headers (*.h) /// - [Required] public string MonoRuntimeHeaders { get; set; } = ""!; /// @@ -167,10 +166,52 @@ public class AppleAppBuilderTask : Task /// public bool StripSymbolTable { get; set; } + /// + /// Bundles the application for NativeAOT runtime. Default runtime is Mono. + /// + public bool UseNativeAOTRuntime { get; set; } + + public void ValidateRuntimeSelection() + { + if (UseNativeAOTRuntime) + { + if (!string.IsNullOrEmpty(MonoRuntimeHeaders)) + throw new ArgumentException($"Property \"{nameof(MonoRuntimeHeaders)}\" is not supported with NativeAOT runtime and will be ignored."); + + if (!string.IsNullOrEmpty(MainLibraryFileName)) + throw new ArgumentException($"Property \"{nameof(MainLibraryFileName)}\" is not supported with NativeAOT runtime and will be ignored."); + + if (UseConsoleUITemplate) + throw new ArgumentException($"Property \"{nameof(UseConsoleUITemplate)}\" is not supported with NativeAOT runtime and will be ignored."); + + if (ForceInterpreter) + throw new ArgumentException($"Property \"{nameof(ForceInterpreter)}\" is not supported with NativeAOT runtime and will be ignored."); + + if (ForceAOT) + throw new ArgumentException($"Property \"{nameof(ForceAOT)}\" is not supported with NativeAOT runtime and will be ignored."); + + if (!string.IsNullOrEmpty(RuntimeComponents)) + throw new ArgumentException($"Property \"{nameof(RuntimeComponents)}\" is not supported with NativeAOT runtime and will be ignored."); + + if (!string.IsNullOrEmpty(DiagnosticPorts)) + throw new ArgumentException($"Property \"{nameof(DiagnosticPorts)}\" is not supported with NativeAOT runtime and will be ignored."); + + if (EnableRuntimeLogging) + throw new ArgumentException($"Property \"{nameof(EnableRuntimeLogging)}\" is not supported with NativeAOT runtime and will be ignored."); + } + else + { + if (string.IsNullOrEmpty(MonoRuntimeHeaders)) + throw new ArgumentException($"The \"{nameof(AppleAppBuilderTask)}\" task was not given a value for the required parameter \"{nameof(MonoRuntimeHeaders)}\" when using Mono runtime."); + } + } + public override bool Execute() { bool isDevice = (TargetOS == TargetNames.iOS || TargetOS == TargetNames.tvOS); + ValidateRuntimeSelection(); + if (!string.IsNullOrEmpty(MainLibraryFileName)) { if (!File.Exists(Path.Combine(AppDir, MainLibraryFileName))) @@ -226,7 +267,7 @@ public class AppleAppBuilderTask : Task } } - if (!ForceInterpreter && (isDevice || ForceAOT) && assemblerFiles.Count == 0) + if (!ForceInterpreter && (isDevice || ForceAOT) && (assemblerFiles.Count == 0 && !UseNativeAOTRuntime)) { throw new InvalidOperationException("Need list of AOT files for device builds."); } @@ -256,7 +297,7 @@ public class AppleAppBuilderTask : Task if (GenerateXcodeProject) { XcodeProjectPath = generator.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink, - AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource); + AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime); if (BuildAppBundle) { @@ -274,7 +315,7 @@ public class AppleAppBuilderTask : Task else if (GenerateCMakeProject) { generator.GenerateCMake(ProjectName, MainLibraryFileName, assemblerFiles, assemblerDataFiles, assemblerFilesToLink, - AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource); + AppDir, binDir, MonoRuntimeHeaders, !isDevice, UseConsoleUITemplate, ForceAOT, ForceInterpreter, InvariantGlobalization, Optimized, EnableRuntimeLogging, EnableAppSandbox, DiagnosticPorts, RuntimeComponents, NativeMainSource, UseNativeAOTRuntime); } return true; diff --git a/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template b/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template index 0697c3f..aba0797 100644 --- a/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template +++ b/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template @@ -10,17 +10,25 @@ set(APP_RESOURCES add_executable( %ProjectName% %MainSource% - runtime.h - runtime.m - %AotModulesSource% ${APP_RESOURCES} ) +if(NOT %UseNativeAOTRuntime%) + target_sources( + %ProjectName% + PRIVATE + %AotModulesSource% + runtime.h + runtime.m) +endif() + %AotSources% %Defines% -include_directories("%MonoInclude%") +if(NOT %UseNativeAOTRuntime%) + include_directories("%MonoInclude%") +endif() set_target_properties(%ProjectName% %AotTargetsList% PROPERTIES XCODE_ATTRIBUTE_SUPPORTS_MACCATALYST "YES" diff --git a/src/tasks/AppleAppBuilder/Templates/main-simple.m b/src/tasks/AppleAppBuilder/Templates/main-simple.m index e05868f..270bf23 100644 --- a/src/tasks/AppleAppBuilder/Templates/main-simple.m +++ b/src/tasks/AppleAppBuilder/Templates/main-simple.m @@ -2,7 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. #import +#if !USE_NATIVE_AOT #import "runtime.h" +#else +extern void* NativeAOT_StaticInitialization(); +extern int __managed__Main(int argc, char* argv[]); +#endif @interface ViewController : UIViewController @end @@ -46,7 +51,15 @@ void (*clickHandlerPtr)(void); [self.view addSubview:button]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ +#if !USE_NATIVE_AOT mono_ios_runtime_init (); +#else +#if INVARIANT_GLOBALIZATION + setenv ("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", TRUE); +#endif + NativeAOT_StaticInitialization(); + int ret_val = __managed__Main(0, NULL); +#endif }); } -(void) buttonClicked:(UIButton*)sender diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs index 87ee142..66d40e5 100644 --- a/src/tasks/AppleAppBuilder/Xcode.cs +++ b/src/tasks/AppleAppBuilder/Xcode.cs @@ -152,10 +152,11 @@ internal sealed class Xcode bool enableRuntimeLogging, bool enableAppSandbox, string? diagnosticPorts, - string? runtimeComponents=null, - string? nativeMainSource = null) + string? runtimeComponents = null, + string? nativeMainSource = null, + bool useNativeAOTRuntime = false) { - var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmDataFiles, asmLinkFiles, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, optimized, enableRuntimeLogging, enableAppSandbox, diagnosticPorts, runtimeComponents, nativeMainSource); + var cmakeDirectoryPath = GenerateCMake(projectName, entryPointLib, asmFiles, asmDataFiles, asmLinkFiles, workspace, binDir, monoInclude, preferDylibs, useConsoleUiTemplate, forceAOT, forceInterpreter, invariantGlobalization, optimized, enableRuntimeLogging, enableAppSandbox, diagnosticPorts, runtimeComponents, nativeMainSource, useNativeAOTRuntime); CreateXcodeProject(projectName, cmakeDirectoryPath); return Path.Combine(binDir, projectName, projectName + ".xcodeproj"); } @@ -210,8 +211,9 @@ internal sealed class Xcode bool enableRuntimeLogging, bool enableAppSandbox, string? diagnosticPorts, - string? runtimeComponents=null, - string? nativeMainSource = null) + string? runtimeComponents = null, + string? nativeMainSource = null, + bool useNativeAOTRuntime = false) { // bundle everything as resources excluding native files var excludes = new List { ".dll.o", ".dll.s", ".dwarf", ".m", ".h", ".a", ".bc", "libmonosgen-2.0.dylib", "libcoreclr.dylib" }; @@ -270,6 +272,7 @@ internal sealed class Xcode 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") + .Replace("%UseNativeAOTRuntime%", useNativeAOTRuntime ? "TRUE" : "FALSE") .Replace("%ProjectName%", projectName) .Replace("%AppResources%", appResources) .Replace("%MainSource%", nativeMainSource) @@ -332,7 +335,12 @@ internal sealed class Xcode // 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 (forceAOT || !(preferDylibs && dylibExists)) + 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 @@ -394,6 +402,11 @@ internal sealed class Xcode defines.AppendLine($"\nadd_definitions(-DDIAGNOSTIC_PORTS=\"{diagnosticPorts}\")"); } + if (useNativeAOTRuntime) + { + defines.AppendLine("add_definitions(-DUSE_NATIVE_AOT=1)"); + } + cmakeLists = cmakeLists.Replace("%Defines%", defines.ToString()); string plist = Utils.GetEmbeddedResource("Info.plist.template") @@ -417,28 +430,31 @@ internal sealed class Xcode File.WriteAllText(Path.Combine(binDir, "app.entitlements"), entitlementsTemplate.Replace("%Entitlements%", ent.ToString())); } - File.WriteAllText(Path.Combine(binDir, "runtime.h"), - Utils.GetEmbeddedResource("runtime.h")); - - // forward pinvokes to "__Internal" - var dllMap = new StringBuilder(); - foreach (string aFile in Directory.GetFiles(workspace, "*.a")) + if (!useNativeAOTRuntime) { - string aFileName = Path.GetFileNameWithoutExtension(aFile); - dllMap.AppendLine($" mono_dllmap_insert (NULL, \"{aFileName}\", NULL, \"__Internal\", NULL);"); + File.WriteAllText(Path.Combine(binDir, "runtime.h"), + Utils.GetEmbeddedResource("runtime.h")); - // also register with or without "lib" prefix - aFileName = aFileName.StartsWith("lib") ? aFileName.Remove(0, 3) : "lib" + aFileName; - dllMap.AppendLine($" mono_dllmap_insert (NULL, \"{aFileName}\", NULL, \"__Internal\", NULL);"); - } + // forward pinvokes to "__Internal" + var dllMap = new StringBuilder(); + foreach (string aFile in Directory.GetFiles(workspace, "*.a")) + { + string aFileName = Path.GetFileNameWithoutExtension(aFile); + dllMap.AppendLine($" mono_dllmap_insert (NULL, \"{aFileName}\", NULL, \"__Internal\", NULL);"); - dllMap.AppendLine($" mono_dllmap_insert (NULL, \"System.Globalization.Native\", NULL, \"__Internal\", NULL);"); + // also register with or without "lib" prefix + aFileName = aFileName.StartsWith("lib") ? aFileName.Remove(0, 3) : "lib" + aFileName; + dllMap.AppendLine($" mono_dllmap_insert (NULL, \"{aFileName}\", NULL, \"__Internal\", NULL);"); + } - File.WriteAllText(Path.Combine(binDir, "runtime.m"), - Utils.GetEmbeddedResource("runtime.m") - .Replace("//%DllMap%", dllMap.ToString()) - .Replace("//%APPLE_RUNTIME_IDENTIFIER%", RuntimeIdentifier) - .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib))); + dllMap.AppendLine($" mono_dllmap_insert (NULL, \"System.Globalization.Native\", NULL, \"__Internal\", NULL);"); + + File.WriteAllText(Path.Combine(binDir, "runtime.m"), + Utils.GetEmbeddedResource("runtime.m") + .Replace("//%DllMap%", dllMap.ToString()) + .Replace("//%APPLE_RUNTIME_IDENTIFIER%", RuntimeIdentifier) + .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib))); + } return binDir; } -- 2.7.4