Enable AppleAppBuilder to bundle iOS applications built for NativeAOT runtime (#81711)
authorIvan Povazan <55002338+ivanpovazan@users.noreply.github.com>
Tue, 7 Feb 2023 18:14:50 +0000 (19:14 +0100)
committerGitHub <noreply@github.com>
Tue, 7 Feb 2023 18:14:50 +0000 (19:14 +0100)
Fixes: https://github.com/dotnet/runtime/issues/80910

src/tasks/AppleAppBuilder/AppleAppBuilder.cs
src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
src/tasks/AppleAppBuilder/Templates/main-simple.m
src/tasks/AppleAppBuilder/Xcode.cs

index 859db0b..6182f97 100644 (file)
@@ -46,7 +46,6 @@ public class AppleAppBuilderTask : Task
     /// <summary>
     /// Path to Mono public headers (*.h)
     /// </summary>
-    [Required]
     public string MonoRuntimeHeaders { get; set; } = ""!;
 
     /// <summary>
@@ -167,10 +166,52 @@ public class AppleAppBuilderTask : Task
     /// </summary>
     public bool StripSymbolTable { get; set; }
 
+    /// <summary>
+    /// Bundles the application for NativeAOT runtime. Default runtime is Mono.
+    /// </summary>
+    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;
index 0697c3f..aba0797 100644 (file)
@@ -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"
index e05868f..270bf23 100644 (file)
@@ -2,7 +2,12 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 #import <UIKit/UIKit.h>
+#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
index 87ee142..66d40e5 100644 (file)
@@ -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<string> { ".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;
     }