Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] Add iOS app builder project #34563

Merged
merged 25 commits into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
610b50a
Add iosAppBuilderTasks project
EgorBo Apr 5, 2020
df88839
Escape spaces in ProjectName
EgorBo Apr 5, 2020
f7d8299
Remove add_definitions(-DDEVICE)
EgorBo Apr 5, 2020
08d6e92
Fix typoe Buillder -> Builder, also build the task as part of mono ru…
EgorBo Apr 5, 2020
bafe2e4
Rewrite HelloiOS sample (to use the task)
EgorBo Apr 5, 2020
ee568ec
Fix build
EgorBo Apr 5, 2020
8449e82
Clean up
EgorBo Apr 5, 2020
15a1682
Add license headers
EgorBo Apr 5, 2020
19390d1
Clean up
EgorBo Apr 6, 2020
183482b
dont build app if provisionig is not set
EgorBo Apr 6, 2020
52a3b20
Add ExcludeFromAppDir input argument, remove stopwatch
EgorBo Apr 6, 2020
ee1531e
Merge branch 'master' of github.com:dotnet/runtime into ios-app-builder
EgorBo Apr 6, 2020
fd8e6c6
Drop DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
EgorBo Apr 6, 2020
a80d1e9
Merge branch 'master' of github.com:dotnet/runtime into ios-app-builder
EgorBo Apr 7, 2020
0afb383
Address feedback
EgorBo Apr 7, 2020
22ada4a
Validate input
EgorBo Apr 7, 2020
89f05c1
fix build
EgorBo Apr 7, 2020
be52a9d
Disable GenerateXcodeProject by default
EgorBo Apr 7, 2020
5de1957
Merge branch 'master' of github.com:dotnet/runtime into ios-app-builder
EgorBo Apr 8, 2020
0029a05
move to mono/msbuild
EgorBo Apr 8, 2020
15c013d
Add OutputDirectory property
EgorBo Apr 8, 2020
fdd7f4b
Fix sample
EgorBo Apr 8, 2020
aa6d96a
Minor improvements
EgorBo Apr 10, 2020
5c11b22
fix build issues
EgorBo Apr 13, 2020
bfcd8c9
Merge branch 'master' of github.com:dotnet/runtime into ios-app-builder
EgorBo Apr 13, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/mono/mono.proj
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,36 @@

<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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should better come from runtime pack

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @steveisok @akoeplinger
how do I build the "runtime pack", I've just built my repo with build.sh -c Release -os iOS -arch arm64 -subset Mono+Libs+Libs.Tests and don't see any runtime packs. The only location where I can find mono-2.0 headers and mono-aot-cross is this $(BinDir)

Copy link
Member

@steveisok steveisok Apr 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in a PR. You'll have to update src/mono/netcore/sample/iOS/Program.csproj when it's ready.

CrossCompiler="$(BinDir)cross\mono-aot-cross"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also come from the runtime pack.

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">
Expand Down
138 changes: 138 additions & 0 deletions src/mono/msbuild/AppleAppBuilder/AotCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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, 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:
//.Append("data-outfile=").Append(Path.Combine(binDir, libName + ".aotdata,"))
.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}.");
}
}
187 changes: 187 additions & 0 deletions src/mono/msbuild/AppleAppBuilder/AppleAppBuilder.cs
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;
}
}
25 changes: 25 additions & 0 deletions src/mono/msbuild/AppleAppBuilder/AppleAppBuilder.csproj
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>
Loading