-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[mono] Add iOS app builder project (#34563)
* Add iosAppBuilderTasks project * Escape spaces in ProjectName * Remove add_definitions(-DDEVICE) * Fix typoe Buillder -> Builder, also build the task as part of mono runtime * Rewrite HelloiOS sample (to use the task) * Fix build * Clean up * Add license headers * Clean up * dont build app if provisionig is not set * Add ExcludeFromAppDir input argument, remove stopwatch * Drop DOTNET_SYSTEM_GLOBALIZATION_INVARIANT * Address feedback * Validate input * fix build * Disable GenerateXcodeProject by default * move to mono/msbuild * Add OutputDirectory property * Fix sample * Minor improvements * fix build issues
- Loading branch information
Showing
18 changed files
with
912 additions
and
174 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// 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}."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
// 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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<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> |
Oops, something went wrong.