- 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
--- /dev/null
+.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
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <OutputPath>bin</OutputPath>
+ <IntermediateOutputPath>$(MSBuildThisFileDirectory)/obj/</IntermediateOutputPath>
+ <TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
+ <TargetOS Condition="'$(TargetOS)' == ''">ios</TargetOS>
+ <TargetOS Condition="'$(TargetsiOSSimulator)' == 'true'">iossimulator</TargetOS>
+ <DeployAndRun Condition="'$(DeployAndRun)' == ''">true</DeployAndRun>
+ <RuntimeIdentifier>$(TargetOS)-$(TargetArchitecture)</RuntimeIdentifier>
+ <AppName>HelloiOS</AppName>
+ <StripDebugSymbols Condition="'$(StripDebugSymbols)' == ''">false</StripDebugSymbols>
+ <!-- NativeAOT-specific props -->
+ <NativeLib>static</NativeLib>
+ <CustomNativeMain>true</CustomNativeMain>
+ <UseNativeAOTRuntime Condition="'$(UseNativeAOTRuntime)' == ''">true</UseNativeAOTRuntime>
+ <!-- FIXME: Once we support building System.Globalization.Native and icu, should be removed -->
+ <InvariantGlobalization>true</InvariantGlobalization>
+ <!-- FIXME: We do not use publish targets yet, but we need to create a publish directory -->
+ <PublishDir Condition="'$(PublishDir)' == ''">$(OutputPath)/publish</PublishDir>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="..\iOS\Program.cs" Link="Program.cs" />
+ <DirectPInvoke Include="__Internal" />
+ </ItemGroup>
+
+ <PropertyGroup Condition="'$(TargetOS)' == 'maccatalyst'">
+ <DevTeamProvisioning Condition="'$(DevTeamProvisioning)' == ''">adhoc</DevTeamProvisioning>
+ <EnableAppSandbox Condition="'$(EnableAppSandbox)' == ''">false</EnableAppSandbox>
+ </PropertyGroup>
+
+ <Import Project="$(CoreClrProjectRoot)nativeaot\BuildIntegration\Microsoft.NETCore.Native.targets" />
+ <UsingTask TaskName="AppleAppBuilderTask"
+ AssemblyFile="$(AppleAppBuilderTasksAssemblyPath)" />
+
+ <!-- FIXME: Once we set up builing appropriate runtime package for iOS-like platforms the following properties should be removed -->
+ <Target Name="ConfigureIlcPathsForiOSCrossCompilation">
+ <PropertyGroup>
+ <IlcPath>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts', 'bin', 'coreclr', '$(HostOS).$(BuildArchitecture).$(CoreCLRConfiguration)', 'ilc'))</IlcPath>
+ <IlcToolsPath>$(IlcPath)</IlcToolsPath>
+ <IlcSdkPath>$(CoreCLRAotSdkDir)</IlcSdkPath>
+ <IlcFrameworkPath>$(LibrariesAllBinArtifactsPath)</IlcFrameworkPath>
+ <IlcFrameworkNativePath>$(LibrariesAllBinArtifactsPath)</IlcFrameworkNativePath>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="BuildAppBundle"
+ AfterTargets="Build"
+ DependsOnTargets="ConfigureIlcPathsForiOSCrossCompilation;SetupProperties;ComputeIlcCompileInputs;IlcCompile">
+
+ <PropertyGroup>
+ <AppDir>$(MSBuildThisFileDirectory)$(PublishDir)\app</AppDir>
+ <IosSimulator Condition="'$(TargetsiOSSimulator)' == 'true'">iPhone 11</IosSimulator>
+ <Optimized Condition="'$(Configuration)' == 'Release'">True</Optimized>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <NativeLibrary Include="%(ManagedBinary.IlcOutputFile)" />
+ </ItemGroup>
+
+ <RemoveDir Directories="$(AppDir)" />
+
+ <AppleAppBuilderTask
+ UseNativeAOTRuntime="$(UseNativeAOTRuntime)"
+ NativeDependencies="@(NativeLibrary)"
+ TargetOS="$(TargetOS)"
+ Arch="$(TargetArchitecture)"
+ ProjectName="$(AppName)"
+ Assemblies="@(BundleAssemblies)"
+ GenerateXcodeProject="True"
+ BuildAppBundle="True"
+ DevTeamProvisioning="$(DevTeamProvisioning)"
+ OutputDirectory="$(AppDir)"
+ Optimized="$(Optimized)"
+ InvariantGlobalization="$(InvariantGlobalization)"
+ StripSymbolTable="$(StripDebugSymbols)"
+ AppDir="$(MSBuildThisFileDirectory)$(PublishDir)" >
+ <Output TaskParameter="AppBundlePath" PropertyName="AppBundlePath" />
+ <Output TaskParameter="XcodeProjectPath" PropertyName="XcodeProjectPath" />
+ </AppleAppBuilderTask>
+
+ <Message Importance="High" Text="Xcode: $(XcodeProjectPath)"/>
+ <Message Importance="High" Text="App: $(AppBundlePath)"/>
+
+ </Target>
+
+ <Target Name="RunAppBundle"
+ AfterTargets="BuildAppBundle"
+ Condition="'$(DeployAndRun)' == 'true'">
+ <Exec Condition="'$(TargetOS)' == 'iossimulator'" Command="dotnet xharness apple run --app=$(AppBundlePath) --targets=ios-simulator-64 --output-directory=/tmp/out" />
+ <Exec Condition="'$(TargetOS)' == 'ios'" Command="dotnet xharness apple run --app=$(AppBundlePath) --targets=ios-device --output-directory=/tmp/out" />
+ </Target>
+
+</Project>
\ No newline at end of file
--- /dev/null
+# 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.
delegate* unmanaged<void> 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
/// </summary>
public bool UseNativeAOTRuntime { get; set; }
+ /// <summary>
+ /// Extra native dependencies to link into the app
+ /// </summary>
+ public string[] NativeDependencies { get; set; } = Array.Empty<string>();
+
public void ValidateRuntimeSelection()
{
if (UseNativeAOTRuntime)
}
}
+ 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.");
%ProjectName%
%MainSource%
${APP_RESOURCES}
+ util.h
+ util.m
)
if(NOT %UseNativeAOTRuntime%)
"-lz"
"-lc++"
"-liconv"
-%NativeLibrariesToLink%
+ %NativeLibrariesToLink%
)
+
+if(%UseNativeAOTRuntime%)
+target_link_libraries(
+ %ProjectName%
+ "-Wl,-u,_NativeAOT_StaticInitialization"
+)
+endif()
\ No newline at end of file
#if !USE_NATIVE_AOT
#import "runtime.h"
#else
-extern void* NativeAOT_StaticInitialization();
+#import <os/log.h>
+#import "util.h"
extern int __managed__Main(int argc, char* argv[]);
#endif
#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
});
}
#include <stdlib.h>
#include <stdio.h>
+#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)
{
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;
mono_jit_cleanup (domain);
+ free_managed_args (&managed_argv, argi);
+
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.
+
+#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 */
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#import <Foundation/Foundation.h>
+
+//---------------------------------------------------------------------------------------
+//
+// 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);
+ }
+}
.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;
}