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; }