<Import Project="Directory.Build.targets" />
+ <Target Name="BuildAppleAppBuilder">
+ <MSBuild Projects="$(MonoProjectRoot)msbuild\AppleAppBuilder\AppleAppBuilder.csproj"
+ Properties="Configuration=$(Configuration)"
+ Targets="Restore;Build" />
+ </Target>
+
+ <UsingTask TaskName="AppleAppBuilderTask"
+ AssemblyFile="$(ArtifactsObjDir)mono\AppleAppBuilder\$(TargetArchitecture)\$(Configuration)\AppleAppBuilder.dll" />
+
+ <Target Name="BuildAppleApp" DependsOnTargets="BuildAppleAppBuilder">
+ <AppleAppBuilderTask
+ Arch="$(TargetArchitecture)"
+ ProjectName="$(ProjectName)"
+ Optimized="$(Optimized)"
+ MonoInclude="$(BinDir)include\mono-2.0"
+ CrossCompiler="$(BinDir)cross\mono-aot-cross"
+ MainLibraryFileName="$(MainLibraryFileName)"
+ NativeMainSource="$(NativeMainSource)"
+ GenerateXcodeProject="$(GenerateXcodeProject)"
+ BuildAppBundle="$(BuildAppBundle)"
+ DevTeamProvisioning="$(DevTeamProvisioning)"
+ OutputDirectory="$(BinDir)\$(ProjectName)"
+ AppDir="$(AppDir)">
+ <Output TaskParameter="AppBundlePath" PropertyName="AppBundlePath" />
+ <Output TaskParameter="XcodeProjectPath" PropertyName="XcodeProjectPath" />
+ </AppleAppBuilderTask>
+ <Message Importance="High" Text="Xcode: $(XcodeProjectPath)"/>
+ <Message Importance="High" Text="App: $(AppBundlePath)"/>
+ </Target>
+
<!-- Ordering matters! Overwriting the Build target. -->
<!-- General targets -->
<Target Name="Build" DependsOnTargets="BuildMonoRuntimeUnix;BuildMonoRuntimeWindows">
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+
+internal class AotCompiler
+{
+ /// <summary>
+ /// Precompile all assemblies in parallel
+ /// </summary>
+ public static void PrecompileLibraries(
+ string crossCompiler,
+ string arch,
+ bool parallel,
+ string binDir,
+ string[] libsToPrecompile,
+ IDictionary<string, string> envVariables,
+ bool optimize)
+ {
+ Parallel.ForEach(libsToPrecompile,
+ new ParallelOptions { MaxDegreeOfParallelism = parallel ? Environment.ProcessorCount : 1 },
+ lib => PrecompileLibrary(crossCompiler, arch, binDir, lib, envVariables, optimize));
+ }
+
+ private static void PrecompileLibrary(
+ string crossCompiler,
+ string arch,
+ string binDir,
+ string libToPrecompile,
+ IDictionary<string, string> envVariables,
+ bool optimize)
+ {
+ Utils.LogInfo($"[AOT] {libToPrecompile}");
+
+ var crossArgs = new StringBuilder();
+ crossArgs
+ .Append(" -O=gsharedvt,float32")
+ .Append(" --nollvm")
+ .Append(" --debug");
+
+ string libName = Path.GetFileNameWithoutExtension(libToPrecompile);
+ var aotArgs = new StringBuilder();
+ aotArgs
+ .Append("mtriple=").Append(arch).Append("-ios,")
+ .Append("static,")
+ .Append("asmonly,")
+ .Append("direct-icalls,")
+ .Append("no-direct-calls,")
+ .Append("dwarfdebug,")
+ .Append("outfile=").Append(Path.Combine(binDir, libName + ".dll.s,"))
+ // TODO: enable aotdata
+ //.Append("data-outfile=").Append(Path.Combine(binDir, libName + ".aotdata,"))
+ // TODO: enable direct-pinvokes (to get rid of -force_loads)
+ //.Append("direct-pinvoke,")
+ .Append("full,");
+
+ // TODO: enable Interpreter
+ // TODO: enable LLVM
+ // TODO: enable System.Runtime.Intrinsics.Arm (LLVM-only for now)
+ // e.g. .Append("mattr=+crc,")
+
+ crossArgs
+ .Append(" --aot=").Append(aotArgs).Append(" ")
+ .Append(libToPrecompile);
+
+ Utils.RunProcess(crossCompiler, crossArgs.ToString(), envVariables, binDir);
+
+ var clangArgs = new StringBuilder();
+ if (optimize)
+ {
+ clangArgs.Append(" -Os");
+ }
+ clangArgs
+ .Append(" -isysroot ").Append(Xcode.Sysroot)
+ .Append(" -miphoneos-version-min=10.1")
+ .Append(" -arch ").Append(arch)
+ .Append(" -c ").Append(Path.Combine(binDir, libName)).Append(".dll.s")
+ .Append(" -o ").Append(Path.Combine(binDir, libName)).Append(".dll.o");
+
+ Utils.RunProcess("clang", clangArgs.ToString(), workingDir: binDir);
+ }
+
+ public static void GenerateLinkAllFile(string[] objFiles, string outputFile)
+ {
+ // Generates 'modules.m' in order to register all managed libraries
+ //
+ //
+ // extern void *mono_aot_module_Lib1_info;
+ // extern void *mono_aot_module_Lib2_info;
+ // ...
+ //
+ // void mono_ios_register_modules (void)
+ // {
+ // mono_aot_register_module (mono_aot_module_Lib1_info);
+ // mono_aot_register_module (mono_aot_module_Lib2_info);
+ // ...
+ // }
+
+ Utils.LogInfo("Generating 'modules.m'...");
+
+ var lsDecl = new StringBuilder();
+ lsDecl
+ .AppendLine("#include <mono/jit/jit.h>")
+ .AppendLine("#include <TargetConditionals.h>")
+ .AppendLine()
+ .AppendLine("#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR")
+ .AppendLine();
+
+ var lsUsage = new StringBuilder();
+ lsUsage
+ .AppendLine("void mono_ios_register_modules (void)")
+ .AppendLine("{");
+ foreach (string objFile in objFiles)
+ {
+ string symbol = "mono_aot_module_" +
+ Path.GetFileName(objFile)
+ .Replace(".dll.o", "")
+ .Replace(".", "_")
+ .Replace("-", "_") + "_info";
+
+ lsDecl.Append("extern void *").Append(symbol).Append(';').AppendLine();
+ lsUsage.Append("\tmono_aot_register_module (").Append(symbol).Append(");").AppendLine();
+ }
+ lsDecl
+ .AppendLine()
+ .Append(lsUsage)
+ .AppendLine("}")
+ .AppendLine()
+ .AppendLine("#endif")
+ .AppendLine();
+
+ File.WriteAllText(outputFile, lsDecl.ToString());
+ Utils.LogInfo($"Saved to {outputFile}.");
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+public class AppleAppBuilderTask : Task
+{
+ /// <summary>
+ /// Path to arm64 AOT cross-compiler (mono-aot-cross)
+ /// It's not used for x64 (Simulator)
+ /// </summary>
+ public string? CrossCompiler { get; set; }
+
+ /// <summary>
+ /// ProjectName is used as an app name, bundleId and xcode project name
+ /// </summary>
+ [Required]
+ public string ProjectName { get; set; } = ""!;
+
+ /// <summary>
+ /// Target directory with *dll and other content to be AOT'd and/or bundled
+ /// </summary>
+ [Required]
+ public string AppDir { get; set; } = ""!;
+
+ /// <summary>
+ /// Path to Mono public headers (*.h)
+ /// </summary>
+ [Required]
+ public string MonoInclude { get; set; } = ""!;
+
+ /// <summary>
+ /// This library will be used as an entry-point (e.g. TestRunner.dll)
+ /// </summary>
+ [Required]
+ public string MainLibraryFileName { get; set; } = ""!;
+
+ /// <summary>
+ /// Path to store build artifacts
+ /// </summary>
+ public string? OutputDirectory { get; set; }
+
+ /// <summary>
+ /// Produce optimized binaries (e.g. use -O2 in AOT)
+ /// and use 'Release' config in xcode
+ /// </summary>
+ public bool Optimized { get; set; }
+
+ /// <summary>
+ /// Disable parallel AOT compilation
+ /// </summary>
+ public bool DisableParallelAot { get; set; }
+
+ /// <summary>
+ /// Target arch, can be "arm64" (device) or "x64" (simulator) at the moment
+ /// </summary>
+ [Required]
+ public string Arch { get; set; } = ""!;
+
+ /// <summary>
+ /// DEVELOPER_TEAM provisioning, needed for arm64 builds.
+ /// </summary>
+ public string? DevTeamProvisioning { get; set; }
+
+ /// <summary>
+ /// Build *.app bundle (using XCode for now)
+ /// </summary>
+ public bool BuildAppBundle { get; set; }
+
+ /// <summary>
+ /// Generate xcode project
+ /// </summary>
+ public bool GenerateXcodeProject { get; set; }
+
+ /// <summary>
+ /// Files to be ignored in AppDir
+ /// </summary>
+ public ITaskItem[]? ExcludeFromAppDir { get; set; }
+
+ /// <summary>
+ /// Path to a custom main.m with custom UI
+ /// A default one is used if it's not set
+ /// </summary>
+ public string? NativeMainSource { get; set; }
+
+ /// <summary>
+ /// Use Console-style native UI template
+ /// (use NativeMainSource to override)
+ /// </summary>
+ public bool UseConsoleUITemplate { get; set; }
+
+ /// <summary>
+ /// Path to *.app bundle
+ /// </summary>
+ [Output]
+ public string AppBundlePath { get; set; } = ""!;
+
+ /// <summary>
+ /// Path to xcode project
+ /// </summary>
+ [Output]
+ public string XcodeProjectPath { get; set; } = ""!;
+
+ public override bool Execute()
+ {
+ Utils.Logger = Log;
+ bool isDevice = Arch.Equals("arm64", StringComparison.InvariantCultureIgnoreCase);
+ if (isDevice && string.IsNullOrEmpty(CrossCompiler))
+ {
+ throw new ArgumentException("arm64 arch requires CrossCompiler");
+ }
+
+ if (!File.Exists(Path.Combine(AppDir, MainLibraryFileName)))
+ {
+ throw new ArgumentException($"MainLibraryFileName='{MainLibraryFileName}' was not found in AppDir='{AppDir}'");
+ }
+
+ // escape spaces
+ ProjectName = ProjectName.Replace(" ", "-");
+
+ string[] excludes = new string[0];
+ if (ExcludeFromAppDir != null)
+ {
+ excludes = ExcludeFromAppDir
+ .Where(i => !string.IsNullOrEmpty(i.ItemSpec))
+ .Select(i => i.ItemSpec)
+ .ToArray();
+ }
+ string[] libsToAot = Directory.GetFiles(AppDir, "*.dll")
+ .Where(f => !excludes.Contains(Path.GetFileName(f)))
+ .ToArray();
+
+ string binDir = Path.Combine(AppDir, $"bin-{ProjectName}-{Arch}");
+ if (!string.IsNullOrEmpty(OutputDirectory))
+ {
+ binDir = OutputDirectory;
+ }
+ Directory.CreateDirectory(binDir);
+
+ // run AOT compilation only for devices
+ if (isDevice)
+ {
+ if (string.IsNullOrEmpty(CrossCompiler))
+ throw new InvalidOperationException("cross-compiler is not set");
+
+ AotCompiler.PrecompileLibraries(CrossCompiler, Arch, !DisableParallelAot, binDir, libsToAot,
+ new Dictionary<string, string> { {"MONO_PATH", AppDir} },
+ Optimized);
+ }
+
+ // generate modules.m
+ AotCompiler.GenerateLinkAllFile(
+ Directory.GetFiles(binDir, "*.dll.o"),
+ Path.Combine(binDir, "modules.m"));
+
+ if (GenerateXcodeProject)
+ {
+ XcodeProjectPath = Xcode.GenerateXCode(ProjectName, MainLibraryFileName,
+ AppDir, binDir, MonoInclude, UseConsoleUITemplate, NativeMainSource);
+
+ if (BuildAppBundle)
+ {
+ if (isDevice && string.IsNullOrEmpty(DevTeamProvisioning))
+ {
+ // DevTeamProvisioning shouldn't be empty for arm64 builds
+ Utils.LogInfo("DevTeamProvisioning is not set, BuildAppBundle step is skipped.");
+ }
+ else
+ {
+ AppBundlePath = Xcode.BuildAppBundle(
+ Path.Combine(binDir, ProjectName, ProjectName + ".xcodeproj"),
+ Arch, Optimized, DevTeamProvisioning);
+ }
+ }
+ Utils.LogInfo($"Xcode: {XcodeProjectPath}\n App: {AppBundlePath}");
+ }
+
+ return true;
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ <OutputPath>bin</OutputPath>
+ <TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
+ <Nullable>enable</Nullable>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ </PropertyGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Templates\*.*" />
+ </ItemGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
+ <PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
+ <PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(RefOnlyMicrosoftBuildTasksCoreVersion)" />
+ <PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(RefOnlyMicrosoftBuildUtilitiesCoreVersion)" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AotCompiler.cs" />
+ <Compile Include="AppleAppBuilder.cs" />
+ <Compile Include="Utils.cs" />
+ <Compile Include="Xcode.cs" />
+ </ItemGroup>
+</Project>
--- /dev/null
+cmake_minimum_required(VERSION 3.14.5)
+
+project(%ProjectName%)
+
+set(APP_RESOURCES
+%AppResources%
+)
+
+add_executable(
+ %ProjectName%
+ %MainSource%
+ runtime.h
+ runtime.m
+ modules.m
+ ${APP_RESOURCES}
+)
+
+include_directories("%MonoInclude%")
+
+set_target_properties(%ProjectName% PROPERTIES
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
+ XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
+ XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING "NO"
+ RESOURCE "${APP_RESOURCES}"
+)
+
+# FIXME: `XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING` should not be NO
+
+target_link_libraries(
+ %ProjectName%
+ "-framework Foundation"
+ "-framework Security"
+ "-framework UIKit"
+ "-framework GSS"
+ "-lz"
+ "-liconv"
+%NativeLibrariesToLink%
+)
<key>CFBundleDevelopmentRegion</key>
<string>en-US</string>
<key>CFBundleExecutable</key>
- <string>HelloiOS</string>
+ <string>%BundleIdentifier%</string>
<key>CFBundleIdentifier</key>
- <string>net.dot.HelloiOS</string>
+ <string>net.dot.%BundleIdentifier%</string>
+ <key>CFBundleName</key>
+ <string>%BundleIdentifier%</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
- <key>CFBundleName</key>
- <string>HelloiOS</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#import <UIKit/UIKit.h>
+#import "runtime.h"
+#include <TargetConditionals.h>
+
+@interface ViewController : UIViewController
+@end
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+@property (strong, nonatomic) UIWindow *window;
+@property (strong, nonatomic) ViewController *controller;
+@end
+
+@implementation AppDelegate
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ self.controller = [[ViewController alloc] initWithNibName:nil bundle:nil];
+ self.window.rootViewController = self.controller;
+ [self.window makeKeyAndVisible];
+ return YES;
+}
+@end
+
+UILabel *summaryLabel;
+UITextView* logLabel;
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
+ logLabel = [[UITextView alloc] initWithFrame:
+ CGRectMake(2.0, 50.0, applicationFrame.size.width, applicationFrame.size.height)];
+ logLabel.font = [UIFont systemFontOfSize:9.0];
+ logLabel.backgroundColor = [UIColor blackColor];
+ logLabel.textColor = [UIColor greenColor];
+ logLabel.scrollEnabled = YES;
+ logLabel.alwaysBounceVertical = YES;
+ logLabel.editable = NO;
+ logLabel.clipsToBounds = YES;
+
+ summaryLabel = [[UILabel alloc] initWithFrame: CGRectMake(10.0, 0.0, applicationFrame.size.width, 50)];
+ summaryLabel.textColor = [UIColor whiteColor];
+ summaryLabel.font = [UIFont boldSystemFontOfSize: 14];
+ summaryLabel.numberOfLines = 2;
+ summaryLabel.textAlignment = NSTextAlignmentLeft;
+#ifdef TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+ summaryLabel.text = @"Loading...";
+#else
+ summaryLabel.text = @"Jitting...";
+#endif
+ [self.view addSubview:logLabel];
+ [self.view addSubview:summaryLabel];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ mono_ios_runtime_init ();
+ });
+}
+
+@end
+
+// can be called from C# to update UI
+void
+mono_ios_set_summary (const char* value)
+{
+ NSString* nsstr = [NSString stringWithUTF8String:strdup(value)];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ summaryLabel.text = nsstr;
+ });
+}
+
+// can be called from C# to update UI
+void
+mono_ios_append_output (const char* value)
+{
+ NSString* nsstr = [NSString stringWithUTF8String:strdup(value)];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ logLabel.text = [logLabel.text stringByAppendingString:nsstr];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [logLabel scrollRangeToVisible: NSMakeRange(logLabel.text.length -1, 1)];
+ });
+ });
+}
+
+int main(int argc, char * argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#import <UIKit/UIKit.h>
+#import "runtime.h"
+
+@interface ViewController : UIViewController
+@end
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+@property (strong, nonatomic) UIWindow *window;
+@property (strong, nonatomic) ViewController *controller;
+@end
+
+@implementation AppDelegate
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ self.controller = [[ViewController alloc] initWithNibName:nil bundle:nil];
+ self.window.rootViewController = self.controller;
+ [self.window makeKeyAndVisible];
+ return YES;
+}
+@end
+
+UILabel *label;
+void (*clickHandlerPtr)(void);
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ label = [[UILabel alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ label.textColor = [UIColor greenColor];
+ label.font = [UIFont boldSystemFontOfSize: 30];
+ label.numberOfLines = 2;
+ label.textAlignment = NSTextAlignmentCenter;
+ label.text = @"Hello, wire me up!\n(dllimport ios_set_text)";
+ [self.view addSubview:label];
+
+ UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark];
+ [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
+ [button setFrame:CGRectMake(50, 300, 200, 50)];
+ [button setTitle:@"Click me (wire me up)" forState:UIControlStateNormal];
+ [button setExclusiveTouch:YES];
+ [self.view addSubview:button];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ mono_ios_runtime_init ();
+ });
+}
+-(void) buttonClicked:(UIButton*)sender
+{
+ if (clickHandlerPtr)
+ clickHandlerPtr();
+}
+
+@end
+
+// called from C# sample
+void
+ios_register_button_click (void* ptr)
+{
+ clickHandlerPtr = ptr;
+}
+
+// called from C# sample
+void
+ios_set_text (const char* value)
+{
+ NSString* nsstr = [NSString stringWithUTF8String:strdup(value)];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ label.text = nsstr;
+ });
+}
+
+int main(int argc, char * argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef runtime_h
+#define runtime_h
+
+void mono_ios_runtime_init (void);
+
+#endif /* runtime_h */
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
#import <Foundation/Foundation.h>
#include <mono/utils/mono-publib.h>
#include <mono/utils/mono-logger.h>
#include <mono/metadata/exception.h>
#include <mono/jit/jit.h>
#include <mono/jit/mono-private-unstable.h>
-
+#include <TargetConditionals.h>
#import <os/log.h>
#include <sys/stat.h>
#include <sys/mman.h>
+static char *bundle_path;
+
// no-op for iOS and tvOS.
// watchOS is not supported yet.
#define MONO_ENTER_GC_UNSAFE
#define MONO_EXIT_GC_UNSAFE
-static os_log_t stdout_log;
-
-static char *bundle_path;
-
const char *
get_bundle_path (void)
{
if (bundle_path)
return bundle_path;
-
NSBundle* main_bundle = [NSBundle mainBundle];
NSString* path = [main_bundle bundlePath];
bundle_path = strdup ([path UTF8String]);
else
res = snprintf (path, sizeof (path) - 1, "%s/%s", bundle, filename);
assert (res > 0);
-
+
struct stat buffer;
if (stat (path, &buffer) == 0) {
MonoAssembly *assembly = mono_assembly_open (path, NULL);
static void
register_dllmap (void)
{
- mono_dllmap_insert (NULL, "libSystem.Native", NULL, "__Internal", NULL);
- mono_dllmap_insert (NULL, "libSystem.IO.Compression.Native", NULL, "__Internal", NULL);
- mono_dllmap_insert (NULL, "libSystem.Security.Cryptography.Native.Apple", NULL, "__Internal", NULL);
+//%DllMap%
}
+#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
void mono_jit_set_aot_mode (MonoAotMode mode);
-
-#if DEVICE
-extern void *mono_aot_module_Program_info;
-extern void *mono_aot_module_System_Private_CoreLib_info;
-extern void *mono_aot_module_System_Runtime_info;
-extern void *mono_aot_module_System_Runtime_Extensions_info;
-extern void *mono_aot_module_System_Collections_info;
-extern void *mono_aot_module_System_Core_info;
-extern void *mono_aot_module_System_Threading_info;
-extern void *mono_aot_module_System_Threading_Tasks_info;
-extern void *mono_aot_module_System_Linq_info;
-extern void *mono_aot_module_System_Memory_info;
-extern void *mono_aot_module_System_Runtime_InteropServices_info;
-extern void *mono_aot_module_System_Text_Encoding_Extensions_info;
-extern void *mono_aot_module_Microsoft_Win32_Primitives_info;
-extern void *mono_aot_module_System_Console_info;
-extern void *mono_aot_module_Program_info;
-
-void mono_ios_register_modules (void)
-{
- mono_aot_register_module (mono_aot_module_Program_info);
- mono_aot_register_module (mono_aot_module_System_Private_CoreLib_info);
- mono_aot_register_module (mono_aot_module_System_Runtime_info);
- mono_aot_register_module (mono_aot_module_System_Runtime_Extensions_info);
- mono_aot_register_module (mono_aot_module_System_Collections_info);
- mono_aot_register_module (mono_aot_module_System_Core_info);
- mono_aot_register_module (mono_aot_module_System_Threading_info);
- mono_aot_register_module (mono_aot_module_System_Threading_Tasks_info);
- mono_aot_register_module (mono_aot_module_System_Linq_info);
- mono_aot_register_module (mono_aot_module_System_Memory_info);
- mono_aot_register_module (mono_aot_module_System_Runtime_InteropServices_info);
- mono_aot_register_module (mono_aot_module_System_Text_Encoding_Extensions_info);
- mono_aot_register_module (mono_aot_module_Microsoft_Win32_Primitives_info);
- mono_aot_register_module (mono_aot_module_System_Console_info);
- mono_aot_register_module (mono_aot_module_Program_info);
-}
-
-void mono_ios_setup_execution_mode (void)
-{
- mono_jit_set_aot_mode (MONO_AOT_MODE_FULL);
-}
+void mono_ios_register_modules (void);
#endif
void
// setenv ("MONO_LOG_LEVEL", "debug", TRUE);
// setenv ("MONO_LOG_MASK", "all", TRUE);
- stdout_log = os_log_create ("net.dot.mono", "stdout");
+ bool auto_exit = FALSE;
+ id args_array = [[NSProcessInfo processInfo] arguments];
+ assert ([args_array count] <= 128);
+ const char *managed_argv [128];
+ int argi;
+ for (argi = 0; argi < [args_array count]; argi++) {
+ NSString* arg = [args_array objectAtIndex: argi];
+ assert ([arg length] >= 3);
+ if ([arg hasPrefix:@"--setenv="]) {
+ // TODO: setenv
+ } else if ([arg isEqualToString:@"--auto-exit"]) {
+ auto_exit = TRUE;
+ }
+ managed_argv[argi] = [arg UTF8String];
+ }
bool wait_for_debugger = FALSE;
- char* executable = "Program.dll";
const char* bundle = get_bundle_path ();
chdir (bundle);
register_dllmap ();
-#if DEVICE
+#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
// register modules
mono_ios_register_modules ();
- mono_ios_setup_execution_mode ();
+ mono_jit_set_aot_mode (MONO_AOT_MODE_FULL);
#endif
-
+
mono_debug_init (MONO_DEBUG_FORMAT_MONO);
mono_install_assembly_preload_hook (assembly_preload_hook, NULL);
mono_install_load_aot_data_hook (load_aot_data, free_aot_data, NULL);
}
mono_jit_init_version ("dotnet.ios", "mobile");
-#if DEVICE
+#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
// device runtimes are configured to use lazy gc thread creation
MONO_ENTER_GC_UNSAFE;
mono_gc_init_finalizer_thread ();
MONO_EXIT_GC_UNSAFE;
#endif
+ const char* executable = "%EntryPointLibName%";
MonoAssembly *assembly = load_assembly (executable, NULL);
assert (assembly);
os_log_info (OS_LOG_DEFAULT, "Executable: %{public}s", executable);
- int res = mono_jit_exec (mono_domain_get (), assembly, 1, &executable);
+ int res = mono_jit_exec (mono_domain_get (), assembly, argi, managed_argv);
// Print this so apps parsing logs can detect when we exited
os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", res);
+
+ if (auto_exit)
+ exit (res);
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+internal class Utils
+{
+ public static string GetEmbeddedResource(string file)
+ {
+ using Stream stream = typeof(Utils).Assembly
+ .GetManifestResourceStream("AppleAppBuilder.Templates." + file)!;
+ using var reader = new StreamReader(stream);
+ return reader.ReadToEnd();
+ }
+
+ public static string RunProcess(
+ string path,
+ string args = "",
+ IDictionary<string, string>? envVars = null,
+ string? workingDir = null,
+ bool ignoreErrors = false)
+ {
+ LogInfo($"Running: {path} {args}");
+ var outputBuilder = new StringBuilder();
+ var errorBuilder = new StringBuilder();
+ var processStartInfo = new ProcessStartInfo
+ {
+ FileName = path,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ Arguments = args,
+ };
+
+ if (workingDir != null)
+ {
+ processStartInfo.WorkingDirectory = workingDir;
+ }
+
+ if (envVars != null)
+ {
+ foreach (KeyValuePair<string, string> envVar in envVars)
+ {
+ processStartInfo.EnvironmentVariables[envVar.Key] = envVar.Value;
+ }
+ }
+
+ Process process = Process.Start(processStartInfo)!;
+ process.ErrorDataReceived += (sender, e) =>
+ {
+ LogError(e.Data);
+ outputBuilder.AppendLine(e.Data);
+ errorBuilder.AppendLine(e.Data);
+ };
+ process.OutputDataReceived += (sender, e) =>
+ {
+ LogInfo(e.Data);
+ outputBuilder.AppendLine(e.Data);
+ };
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ process.WaitForExit();
+ if (process.ExitCode != 0)
+ {
+ throw new Exception("Error: " + errorBuilder);
+ }
+
+ return outputBuilder.ToString().Trim('\r','\n');
+ }
+
+ public static TaskLoggingHelper? Logger { get; set; }
+
+ public static void LogInfo(string? msg)
+ {
+ if (msg != null)
+ {
+ Logger?.LogMessage(MessageImportance.High, msg);
+ }
+ }
+
+ public static void LogError(string? msg)
+ {
+ if (msg != null)
+ {
+ Logger?.LogError(msg);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+internal class Xcode
+{
+ public static string Sysroot { get; } = Utils.RunProcess("xcrun", "--sdk iphoneos --show-sdk-path");
+
+ public static string GenerateXCode(
+ string projectName,
+ string entryPointLib,
+ string workspace,
+ string binDir,
+ string monoInclude,
+ bool useConsoleUiTemplate = false,
+ string? nativeMainSource = null)
+ {
+ // bundle everything as resources excluding native files
+ string[] excludes = {".dylib", ".dll.o", ".dll.s", ".dwarf", ".m", ".h", ".a"};
+ string[] resources = Directory.GetFiles(workspace)
+ .Where(f => !excludes.Any(e => f.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
+ .Concat(Directory.GetFiles(binDir, "*.aotdata"))
+ .ToArray();
+
+ if (string.IsNullOrEmpty(nativeMainSource))
+ {
+ // use built-in main.m (with default UI) if it's not set
+ nativeMainSource = Path.Combine(binDir, "main.m");
+ File.WriteAllText(nativeMainSource, Utils.GetEmbeddedResource(useConsoleUiTemplate ? "main-console.m" : "main-simple.m"));
+ }
+ else
+ {
+ string newMainPath = Path.Combine(binDir, "main.m");
+ if (nativeMainSource != newMainPath)
+ {
+ File.Copy(nativeMainSource, Path.Combine(binDir, "main.m"), true);
+ nativeMainSource = newMainPath;
+ }
+ }
+
+ string cmakeLists = Utils.GetEmbeddedResource("CMakeLists.txt.template")
+ .Replace("%ProjectName%", projectName)
+ .Replace("%AppResources%", string.Join(Environment.NewLine, resources.Select(r => " " + r)))
+ .Replace("%MainSource%", nativeMainSource)
+ .Replace("%MonoInclude%", monoInclude);
+
+ string toLink = "";
+ foreach (string lib in Directory.GetFiles(workspace, "*.a"))
+ {
+ // these libraries are pinvoked
+ // -force_load will be removed once we enable direct-pinvokes for AOT
+ toLink += $" \"-force_load {lib}\"{Environment.NewLine}";
+ }
+ foreach (string lib in Directory.GetFiles(binDir, "*.dll.o"))
+ {
+ // these libraries are linked via modules.m
+ toLink += $" \"{lib}\"{Environment.NewLine}";
+ }
+ cmakeLists = cmakeLists.Replace("%NativeLibrariesToLink%", toLink);
+
+ string plist = Utils.GetEmbeddedResource("Info.plist.template")
+ .Replace("%BundleIdentifier%", projectName);
+
+ File.WriteAllText(Path.Combine(binDir, "Info.plist.in"), plist);
+ File.WriteAllText(Path.Combine(binDir, "CMakeLists.txt"), cmakeLists);
+
+ var cmakeArgs = new StringBuilder();
+ cmakeArgs
+ .Append("-S.")
+ .Append(" -B").Append(projectName)
+ .Append(" -GXcode")
+ .Append(" -DCMAKE_SYSTEM_NAME=iOS")
+ .Append(" \"-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64\"")
+ .Append(" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.1")
+ .Append(" -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO");
+
+ File.WriteAllText(Path.Combine(binDir, "runtime.h"),
+ Utils.GetEmbeddedResource("runtime.h"));
+
+ // forward pinvokes to "__Internal"
+ string dllMap = string.Join(Environment.NewLine, Directory.GetFiles(workspace, "*.a")
+ .Select(f => $" mono_dllmap_insert (NULL, \"{Path.GetFileNameWithoutExtension(f)}\", NULL, \"__Internal\", NULL);"));
+
+ File.WriteAllText(Path.Combine(binDir, "runtime.m"),
+ Utils.GetEmbeddedResource("runtime.m")
+ .Replace("//%DllMap%", dllMap)
+ .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib)));
+
+ Utils.RunProcess("cmake", cmakeArgs.ToString(), workingDir: binDir);
+
+ return Path.Combine(binDir, projectName, projectName + ".xcodeproj");
+ }
+
+ public static string BuildAppBundle(
+ string xcodePrjPath, string architecture, bool optimized, string? devTeamProvisioning = null)
+ {
+ string sdk = "";
+ var args = new StringBuilder();
+ args.Append("ONLY_ACTIVE_ARCH=NO");
+ if (architecture == "arm64")
+ {
+ sdk = "iphoneos";
+ args.Append(" -arch arm64")
+ .Append(" -sdk iphoneos")
+ .Append(" -allowProvisioningUpdates")
+ .Append(" DEVELOPMENT_TEAM=").Append(devTeamProvisioning);
+ }
+ else
+ {
+ sdk = "iphonesimulator";
+ args.Append(" -arch x86_64")
+ .Append(" -sdk iphonesimulator");
+ }
+
+ string config = optimized ? "Release" : "Debug";
+ args.Append(" -configuration ").Append(config);
+
+ Utils.RunProcess("xcodebuild", args.ToString(), workingDir: Path.GetDirectoryName(xcodePrjPath));
+ return Path.Combine(Path.GetDirectoryName(xcodePrjPath)!, config + "-" + sdk,
+ Path.GetFileNameWithoutExtension(xcodePrjPath) + ".app");
+ }
+}
+++ /dev/null
-bin/
-xcode/
-*.o
-*.dll
\ No newline at end of file
+++ /dev/null
-cmake_minimum_required(VERSION 3.14.5)
-
-project(HelloiOS)
-
-file(GLOB DLLS *.dll)
-file(GLOB DLLS_AOT *.dll.o)
-file(GLOB DLLS_PDB *.pdb)
-
-set(APP_RESOURCES
- ${DLLS}
- ${DLLS_PDB}
-)
-
-add_executable(
- HelloiOS
- main.m
- runtime.h
- runtime.m
- ${APP_RESOURCES}
-)
-
-if (MONO_ARCH STREQUAL arm64)
- add_definitions(-DDEVICE)
-endif()
-
-include_directories("../../../../../artifacts/bin/mono/iOS.${MONO_ARCH}.${MONO_CONFIG}/include/mono-2.0")
-
-set_target_properties(HelloiOS PROPERTIES
- MACOSX_BUNDLE TRUE
- MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Plist.in
- XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
- XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING "NO"
- RESOURCE "${APP_RESOURCES}"
-)
-
-# FIXME: `XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING` should not be NO
-# investigate why `PALEXPORT` doesn't preserve symbols
-
-target_link_libraries(
- HelloiOS
- "-framework Foundation"
- "-framework Security"
- "-framework UIKit"
- "-framework GSS"
- "-lz"
- "-liconv"
- "-force_load ../../../../../artifacts/bin/mono/iOS.${MONO_ARCH}.${MONO_CONFIG}/libmono.a"
- "-force_load ../../../../../artifacts/bin/native/netcoreapp5.0-iOS-${MONO_CONFIG}-${MONO_ARCH}/libSystem.IO.Compression.Native.a"
- "-force_load ../../../../../artifacts/bin/native/netcoreapp5.0-iOS-${MONO_CONFIG}-${MONO_ARCH}/libSystem.Native.a"
- "-force_load ../../../../../artifacts/bin/native/netcoreapp5.0-iOS-${MONO_CONFIG}-${MONO_ARCH}/libSystem.Net.Security.Native.a"
- "-force_load ../../../../../artifacts/bin/native/netcoreapp5.0-iOS-${MONO_CONFIG}-${MONO_ARCH}/libSystem.Security.Cryptography.Native.Apple.a"
- ${DLLS_AOT}
-)
\ No newline at end of file
MONO_CONFIG=Debug
-
-# change to x64 for simulator
MONO_ARCH=arm64
+
ARTIFACTS_BIN=../../../../../artifacts/bin/
ARTIFACTS_BCL=$(ARTIFACTS_BIN)runtime/netcoreapp5.0-iOS-$(MONO_CONFIG)-$(MONO_ARCH)
ARTIFACTS_MONO=$(ARTIFACTS_BIN)/mono/iOS.$(MONO_ARCH).$(MONO_CONFIG)
DOTNET := $(shell cd ../../ && bash init-tools.sh | tail -1)
-SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
BCL_LIBS = \
System.Runtime.dll \
Microsoft.Win32.Primitives.dll \
System.Console.dll
-all: mono-libraries aot-all xcode
-
-# rebuild mono and libraries for iOS and arm64 or x64
-mono-libraries:
- ../../../../.././build.sh -os iOS -arch $(MONO_ARCH) --subsetCategory mono-libraries
-
-# once a new library is added here it should also be
-# added in mono_ios_register_modules() (runtime.m)
-aot-all: prepare
- make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_MONO)/System.Private.CoreLib.dll
- for lib in $(BCL_LIBS); do make aot-lib-${MONO_ARCH} LIB=$(ARTIFACTS_BCL)/$$lib; done
- make aot-program
+all: mono build
-# recompile Program.cs AOT
-aot-program:
+program:
$(DOTNET) build -c Debug Program.csproj
- make aot-lib-${MONO_ARCH} LIB=bin/Program.dll
-
-# we need to copy some BCL libs to ARTIFACTS_MONO
-# to be able to aot other bcl libs
-prepare:
- for lib in $(BCL_LIBS); do cp $(ARTIFACTS_BCL)/$$lib $(ARTIFACTS_MONO); done
-
-# we'll use regular jit for simulator
-aot-lib-x64:
- cp $(LIB) $(notdir $(LIB))
-
-aot-lib-arm64:
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 MONO_PATH=$(ARTIFACTS_MONO) \
- $(ARTIFACTS_MONO)/cross/./mono-aot-cross -O=gsharedvt,float32 --nollvm --debug \
- --aot=mtriple=arm64-ios,static,asmonly,direct-icalls,no-direct-calls,dwarfdebug,full $(LIB) && \
- clang -isysroot $(SYSROOT) -miphoneos-version-min=10.1 -arch arm64 -c $(LIB).s
- cp $(LIB) $(notdir $(LIB))
-# generate an xcode project
-xcode:
- cmake -S. -BXcode -GXcode \
- -DCMAKE_SYSTEM_NAME=iOS \
- "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" \
- -DCMAKE_OSX_DEPLOYMENT_TARGET=10.1 \
- -DCMAKE_INSTALL_PREFIX=`pwd`/_install \
- -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO \
- -DCMAKE_IOS_INSTALL_COMBINED=YES \
- -DMONO_CONFIG=$(MONO_CONFIG) \
- -DMONO_ARCH=$(MONO_ARCH)
+# copy everything to bin/ and run BuildIosApp task
+build: clean program
+ cp $(ARTIFACTS_MONO)/System.Private.CoreLib.{dll,pdb} bin
+ cp $(ARTIFACTS_MONO)/libmono.a bin
+ for lib in $(BCL_LIBS); do cp $(ARTIFACTS_BCL)/$$lib bin; done
+ cp $(ARTIFACTS_BCL)/*.a bin
+ $(DOTNET) msbuild /t:BuildAppleApp /p:Configuration=$(MONO_CONFIG) \
+ /p:TargetArchitecture=$(MONO_ARCH) \
+ /p:TargetOS=iOS \
+ /p:OutputDirectory=bin \
+ /p:ProjectName=HelloiOS \
+ /p:Optimized=false \
+ /p:AppDir=$(CURDIR)/bin \
+ /p:NativeMainSource=$(CURDIR)/main.m \
+ /p:BuildAppBundle=true \
+ /p:GenerateXcodeProject=true \
+ /p:DevTeamProvisioning= \
+ /p:MainLibraryFileName=Program.dll \
+ ../../../mono.proj
+
+deploy-sim:
+ make build MONO_ARCH=x64
+ xcrun simctl shutdown "iPhone 11" || true
+ xcrun simctl boot "iPhone 11"
+ open -a Simulator
+ xcrun simctl install "iPhone 11" $(realpath ../../../../../artifacts/bin/mono/iOS.x64.$(MONO_CONFIG)/HelloiOS/HelloiOS/Debug-iphonesimulator/HelloiOS.app)
+ xcrun simctl launch booted net.dot.HelloiOS
+
+# rebuild mono for iOS-arm64 or x64
+mono:
+ ../../../../.././build.sh -c $(MONO_CONFIG) -os iOS -arch $(MONO_ARCH) -subset Mono
clean:
- rm -rf *.dll
- rm -rf *.dll.o
- rm -rf Xcode
\ No newline at end of file
+ rm -rf bin
-using System;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
#import <UIKit/UIKit.h>
#import "runtime.h"
+++ /dev/null
-#ifndef runtime_h
-#define runtime_h
-
-void mono_ios_runtime_init (void);
-
-#endif /* runtime_h */