Skip to content

Commit

Permalink
Enable AppleAppBuilder to bundle iOS applications built for NativeAOT…
Browse files Browse the repository at this point in the history
… runtime (#81711)

Fixes: #80910
  • Loading branch information
ivanpovazan authored Feb 7, 2023
1 parent f01d5a0 commit 8642a21
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 32 deletions.
49 changes: 45 additions & 4 deletions src/tasks/AppleAppBuilder/AppleAppBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public string TargetOS
/// <summary>
/// Path to Mono public headers (*.h)
/// </summary>
[Required]
public string MonoRuntimeHeaders { get; set; } = ""!;

/// <summary>
Expand Down Expand Up @@ -167,10 +166,52 @@ public string TargetOS
/// </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)))
Expand Down Expand Up @@ -226,7 +267,7 @@ public override bool Execute()
}
}

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.");
}
Expand Down Expand Up @@ -256,7 +297,7 @@ public override bool Execute()
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)
{
Expand All @@ -274,7 +315,7 @@ public override bool Execute()
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;
Expand Down
16 changes: 12 additions & 4 deletions src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
13 changes: 13 additions & 0 deletions src/tasks/AppleAppBuilder/Templates/main-simple.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -46,7 +51,15 @@ - (void)viewDidLoad {
[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
Expand Down
64 changes: 40 additions & 24 deletions src/tasks/AppleAppBuilder/Xcode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,11 @@ public string GenerateXCode(
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");
}
Expand Down Expand Up @@ -210,8 +211,9 @@ public string GenerateCMake(
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" };
Expand Down Expand Up @@ -270,6 +272,7 @@ public string GenerateCMake(
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)
Expand Down Expand Up @@ -332,7 +335,12 @@ public string GenerateCMake(
// 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
Expand Down Expand Up @@ -394,6 +402,11 @@ public string GenerateCMake(
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")
Expand All @@ -417,28 +430,31 @@ public string GenerateCMake(
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;
}
Expand Down

0 comments on commit 8642a21

Please sign in to comment.