From: Ivan Povazan <55002338+ivanpovazan@users.noreply.github.com>
Date: Fri, 24 Feb 2023 16:42:55 +0000 (+0100)
Subject: Adding HelloiOS sample for NativeAOT (#82249)
X-Git-Tag: accepted/tizen/unified/riscv/20231226.055536~3828
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=78f8010e83b4d0380314dd4301a585a60d0cb604;p=platform%2Fupstream%2Fdotnet%2Fruntime.git
Adding HelloiOS sample for NativeAOT (#82249)
- The application is intended to be used by developers who work on enabling NativeAOT on iOS-like platforms and can serve as PoC
- Currently still relies on dotnet/runtime internals and requires local builds instead of package references
---
diff --git a/src/mono/sample/iOS-NativeAOT/Makefile b/src/mono/sample/iOS-NativeAOT/Makefile
new file mode 100644
index 0000000..e1e295a
--- /dev/null
+++ b/src/mono/sample/iOS-NativeAOT/Makefile
@@ -0,0 +1,50 @@
+.DEFAULT_GOAL := all
+
+TOP=../../../../
+
+BUILD_CONFIG?=Debug
+TARGET_ARCH?=$(shell . $(TOP)eng/native/init-os-and-arch.sh && echo $${arch})
+TARGET_OS?=iossimulator
+DEPLOY_AND_RUN?=false
+STRIP_DEBUG_SYMBOLS?=false
+
+REPO_DIR=$(realpath $(TOP))
+TASKS_DIR=$(REPO_DIR)/src/tasks
+DOTNET=$(REPO_DIR)/dotnet.sh
+BUILD_SCRIPT=$(REPO_DIR)/build.sh
+
+world: build-deps all
+
+# build all dependencies: runtime, nativeAot and all the libs
+build-deps: build-runtime-ilc build-libs-all
+
+# building for host
+build-runtime-ilc:
+ $(BUILD_SCRIPT) clr+clr.aot -c $(BUILD_CONFIG)
+
+build-ilc:
+ $(BUILD_SCRIPT) clr.aot -c $(BUILD_CONFIG)
+
+# building for target platform
+build-libs-all:
+ $(BUILD_SCRIPT) clr.nativeaotruntime+clr.nativeaotlibs+libs -c $(BUILD_CONFIG) -os $(TARGET_OS) -arch $(TARGET_ARCH)
+
+build-libs-nativeaot:
+ $(BUILD_SCRIPT) clr.nativeaotruntime+clr.nativeaotlibs -c $(BUILD_CONFIG) -os $(TARGET_OS) -arch $(TARGET_ARCH)
+
+all: appbuilder hello-app
+
+appbuilder:
+ $(DOTNET) build -c $(BUILD_CONFIG) $(TASKS_DIR)/AppleAppBuilder/AppleAppBuilder.csproj
+
+hello-app: clean
+ $(DOTNET) \
+ build -c $(BUILD_CONFIG) \
+ -p:TargetOS=$(TARGET_OS) \
+ -p:TargetArchitecture=$(TARGET_ARCH) \
+ -p:DeployAndRun=$(DEPLOY_AND_RUN) \
+ -p:StripDebugSymbols=$(STRIP_DEBUG_SYMBOLS) \
+ -bl
+
+clean:
+ rm -rf obj bin
\ No newline at end of file
diff --git a/src/mono/sample/iOS-NativeAOT/Program.csproj b/src/mono/sample/iOS-NativeAOT/Program.csproj
new file mode 100644
index 0000000..aa08743
--- /dev/null
+++ b/src/mono/sample/iOS-NativeAOT/Program.csproj
@@ -0,0 +1,95 @@
+
+
+ Exe
+ bin
+ $(MSBuildThisFileDirectory)/obj/
+ $(NetCoreAppCurrent)
+ ios
+ iossimulator
+ true
+ $(TargetOS)-$(TargetArchitecture)
+ HelloiOS
+ false
+
+ static
+ true
+ true
+
+ true
+
+ $(OutputPath)/publish
+
+
+
+
+
+
+
+
+ adhoc
+ false
+
+
+
+
+
+
+
+
+ $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts', 'bin', 'coreclr', '$(HostOS).$(BuildArchitecture).$(CoreCLRConfiguration)', 'ilc'))
+ $(IlcPath)
+ $(CoreCLRAotSdkDir)
+ $(LibrariesAllBinArtifactsPath)
+ $(LibrariesAllBinArtifactsPath)
+
+
+
+
+
+
+ $(MSBuildThisFileDirectory)$(PublishDir)\app
+ iPhone 11
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/mono/sample/iOS-NativeAOT/README.md b/src/mono/sample/iOS-NativeAOT/README.md
new file mode 100644
index 0000000..fe6fcb1
--- /dev/null
+++ b/src/mono/sample/iOS-NativeAOT/README.md
@@ -0,0 +1,79 @@
+# NativeAOT iOS sample app
+
+## Description
+
+This sample application is intended to be used by developers who work on enabling NativeAOT on iOS-like platforms and can serve as PoC for verifying support for the following systems:
+- ios
+- iossimulator
+- tvos
+- tvossimulator
+- maccatalyst
+
+The sample shares the source code with the Mono sample specified at: `../iOS/Program.cs` and in general should have the same behavior as MonoAOT.
+
+## Limitations
+
+The application is **_currently_** relying on the following:
+1. Internal dependencies - locally building the internals is required as runtime and tools nuget packages are still not being produced
+2. Invariant globalization - `System.Globalization.Native` is currently not being built as part of NativeAOT framework for iOS-like platforms
+3. No publish targets - the SDK and MSBuild integration is still not complete
+
+## How to build and test
+
+### Building for the first time
+
+When building for the first time (on a clean checkout) run from this directory the following `make` command:
+``` bash
+make world
+```
+This will first build all required runtime components and dependencies, after which it will build the sample app and bundle it into an application bundle.
+By default the build will use `Debug` build configuration and target `iossimulator`.
+To change this behavior, specify the desired setting in the following way:
+``` bash
+make world BUILD_CONFIG=Release TARGET_OS=ios
+```
+
+### To avoid building all the dependencies
+
+For future builds, you can run just:
+``` bash
+make
+```
+which will skip building all the runtime dependencies, assuming those have been already properly built, and build the MSBuild task used for bundling the application and the application it self.
+
+For convenience, it is also possible to rebuild only the application it self with:
+``` bash
+make hello-app
+```
+
+### Deploy and run
+
+#### Simulator
+
+To test the application on a simulator include the following in your make command `DEPLOY_AND_RUN=true` e.g.,:
+``` bash
+make hello-app DEPLOY_AND_RUN=true
+```
+
+#### Device
+
+To test the application on a device, a provisioning profile needs to be specified.
+This can be achieved by defining `DevTeamProvisioning` environment variable with a valid team ID (see [developer.apple.com/account/#/membership](https://developer.apple.com/account/#/membership), scroll down to `Team ID`) for example:
+``` bash
+export DevTeamProvisioning=A1B2C3D4E5; make hello-app TARGET_OS=ios DEPLOY_AND_RUN=true
+```
+Assuming `A1B2C3D4E5` is a valid team ID.
+
+#### One-liner
+
+On a clean dotnet/runtime checkout, from this directory, run:
+
+``` bash
+export DevTeamProvisioning=A1B2C3D4E5; make world BUILD_CONFIG=Release TARGET_OS=ios DEPLOY_AND_RUN=true
+```
+
+This command will build everything necessary to run and deploy the application on an iOS device.
+
+### Custom builds
+
+Check the `Makefile` for individual list of targets and variables to customize the build.
diff --git a/src/mono/sample/iOS/Program.cs b/src/mono/sample/iOS/Program.cs
index 603ff2e..1c9839a 100644
--- a/src/mono/sample/iOS/Program.cs
+++ b/src/mono/sample/iOS/Program.cs
@@ -35,7 +35,7 @@ public static class Program
delegate* unmanaged unmanagedPtr = &OnButtonClick;
ios_register_button_click(unmanagedPtr);
}
- const string msg = "Hello World!\n.NET 5.0";
+ const string msg = "Hello World!\n.NET 8.0";
for (int i = 0; i < msg.Length; i++)
{
// a kind of an animation
diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs
index 6182f97..785c406 100644
--- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs
+++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs
@@ -171,6 +171,11 @@ public class AppleAppBuilderTask : Task
///
public bool UseNativeAOTRuntime { get; set; }
+ ///
+ /// Extra native dependencies to link into the app
+ ///
+ public string[] NativeDependencies { get; set; } = Array.Empty();
+
public void ValidateRuntimeSelection()
{
if (UseNativeAOTRuntime)
@@ -267,6 +272,11 @@ public class AppleAppBuilderTask : Task
}
}
+ foreach (var nativeDependency in NativeDependencies)
+ {
+ assemblerFilesToLink.Add(nativeDependency);
+ }
+
if (!ForceInterpreter && (isDevice || ForceAOT) && (assemblerFiles.Count == 0 && !UseNativeAOTRuntime))
{
throw new InvalidOperationException("Need list of AOT files for device builds.");
diff --git a/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template b/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
index aba0797..3053a7e 100644
--- a/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
+++ b/src/tasks/AppleAppBuilder/Templates/CMakeLists.txt.template
@@ -11,6 +11,8 @@ add_executable(
%ProjectName%
%MainSource%
${APP_RESOURCES}
+ util.h
+ util.m
)
if(NOT %UseNativeAOTRuntime%)
@@ -73,5 +75,12 @@ target_link_libraries(
"-lz"
"-lc++"
"-liconv"
-%NativeLibrariesToLink%
+ %NativeLibrariesToLink%
)
+
+if(%UseNativeAOTRuntime%)
+target_link_libraries(
+ %ProjectName%
+ "-Wl,-u,_NativeAOT_StaticInitialization"
+)
+endif()
\ No newline at end of file
diff --git a/src/tasks/AppleAppBuilder/Templates/main-simple.m b/src/tasks/AppleAppBuilder/Templates/main-simple.m
index 270bf23..f34d4c9 100644
--- a/src/tasks/AppleAppBuilder/Templates/main-simple.m
+++ b/src/tasks/AppleAppBuilder/Templates/main-simple.m
@@ -5,7 +5,8 @@
#if !USE_NATIVE_AOT
#import "runtime.h"
#else
-extern void* NativeAOT_StaticInitialization();
+#import
+#import "util.h"
extern int __managed__Main(int argc, char* argv[]);
#endif
@@ -57,8 +58,12 @@ void (*clickHandlerPtr)(void);
#if INVARIANT_GLOBALIZATION
setenv ("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", TRUE);
#endif
- NativeAOT_StaticInitialization();
- int ret_val = __managed__Main(0, NULL);
+ char **managed_argv;
+ int managed_argc = get_managed_args (&managed_argv);
+ int ret_val = __managed__Main (managed_argc, managed_argv);
+ free_managed_args (&managed_argv, managed_argc);
+ os_log_info (OS_LOG_DEFAULT, EXIT_CODE_TAG ": %d", ret_val);
+ exit (ret_val);
#endif
});
}
diff --git a/src/tasks/AppleAppBuilder/Templates/runtime.m b/src/tasks/AppleAppBuilder/Templates/runtime.m
index 1d0cd6f..9cc3879 100644
--- a/src/tasks/AppleAppBuilder/Templates/runtime.m
+++ b/src/tasks/AppleAppBuilder/Templates/runtime.m
@@ -20,15 +20,14 @@
#include
#include
+#import "util.h"
+
static char *bundle_path;
#define APPLE_RUNTIME_IDENTIFIER "//%APPLE_RUNTIME_IDENTIFIER%"
#define RUNTIMECONFIG_BIN_FILE "runtimeconfig.bin"
-// XHarness is looking for this tag in app's output to determine the exit code
-#define EXIT_CODE_TAG "DOTNET.APP_EXIT_CODE"
-
const char *
get_bundle_path (void)
{
@@ -254,14 +253,8 @@ mono_ios_runtime_init (void)
setenv ("DOTNET_DiagnosticPorts", DIAGNOSTIC_PORTS, true);
#endif
- 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];
- managed_argv[argi] = [arg UTF8String];
- }
+ char **managed_argv;
+ int argi = get_managed_args (&managed_argv);
bool wait_for_debugger = FALSE;
@@ -373,5 +366,7 @@ mono_ios_runtime_init (void)
mono_jit_cleanup (domain);
+ free_managed_args (&managed_argv, argi);
+
exit (res);
}
diff --git a/src/tasks/AppleAppBuilder/Templates/util.h b/src/tasks/AppleAppBuilder/Templates/util.h
new file mode 100644
index 0000000..c81697a
--- /dev/null
+++ b/src/tasks/AppleAppBuilder/Templates/util.h
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef util_h
+#define util_h
+
+// XHarness is looking for this tag in app's output to determine the exit code
+#define EXIT_CODE_TAG "DOTNET.APP_EXIT_CODE"
+
+size_t get_managed_args (char*** managed_args_array);
+void free_managed_args (char*** managed_args_array, size_t array_size);
+
+#endif /* util_h */
diff --git a/src/tasks/AppleAppBuilder/Templates/util.m b/src/tasks/AppleAppBuilder/Templates/util.m
new file mode 100644
index 0000000..b80b56d
--- /dev/null
+++ b/src/tasks/AppleAppBuilder/Templates/util.m
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#import
+
+//---------------------------------------------------------------------------------------
+//
+// get_managed_args: converts arguments passed to the Objective-C program to their
+// C-representation so they can be passed to the managed side.
+// The caller is responsible for freeing up the allocated memory.
+// This can be achieved by calling the accompanied 'free_managed_args' function.
+//
+// Arguments:
+// * managed_args_array - pointer to array of strings to hold converted arguments.
+//
+// Return Value:
+// int - number of arguments (size of the array of string)
+//
+size_t get_managed_args (char*** managed_args_array)
+{
+ id args_array = [[NSProcessInfo processInfo] arguments];
+ size_t args_count = [args_array count];
+ assert (args_count <= 128);
+ *managed_args_array = (char**) malloc (sizeof(char*) * args_count);
+ size_t argi;
+ for (argi = 0; argi < args_count; argi++) {
+ NSString* arg = [args_array objectAtIndex: argi];
+ const char* cstring = [arg UTF8String];
+ size_t cstring_len = strlen(cstring) + 1;
+ (*managed_args_array)[argi] = (char*) malloc (sizeof(char) * cstring_len);
+ strcpy((*managed_args_array)[argi], cstring);
+ }
+ return argi;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// free_managed_args: frees up the allocated memory for the program arguments.
+//
+// Arguments:
+// * managed_args_array - pointer to array of strings which converted program arguments
+// * array_size - number of arguments (size of the array of string)
+//
+void free_managed_args (char*** managed_args_array, size_t array_size)
+{
+ if (*managed_args_array != NULL)
+ {
+ for (size_t i = 0; i < array_size; i++)
+ free((*managed_args_array)[i]);
+ free(*managed_args_array);
+ }
+}
diff --git a/src/tasks/AppleAppBuilder/Xcode.cs b/src/tasks/AppleAppBuilder/Xcode.cs
index ea6e0dd..4561df0 100644
--- a/src/tasks/AppleAppBuilder/Xcode.cs
+++ b/src/tasks/AppleAppBuilder/Xcode.cs
@@ -456,6 +456,9 @@ internal sealed class Xcode
.Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib)));
}
+ File.WriteAllText(Path.Combine(binDir, "util.h"), Utils.GetEmbeddedResource("util.h"));
+ File.WriteAllText(Path.Combine(binDir, "util.m"), Utils.GetEmbeddedResource("util.m"));
+
return binDir;
}