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 18 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
133 changes: 133 additions & 0 deletions src/mono/AppleAppBuilder/AotCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 binDir,
string[] libsToPrecompile,
IDictionary<string, string> envVariables,
bool optimize)
{
Parallel.ForEach(libsToPrecompile,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
lib => PrecompileLibrary(crossCompiler, binDir, lib, envVariables, optimize));
}

private static void PrecompileLibrary(
string crossCompiler,
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=arm64-ios,")
.Append("static,")
.Append("asmonly,")
.Append("direct-icalls,")
.Append("no-direct-calls,")
.Append("dwarfdebug,")
.Append("outfile=").Append(Path.Combine(binDir, libName + ".dll.s,"))
.Append("full,");

// TODO: save *.aotdata
// 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 arm64")
.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}.");
}
}
164 changes: 164 additions & 0 deletions src/mono/AppleAppBuilder/AppleAppBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// 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 a custom main.m with custom UI
/// A default one is used if it's not set
/// </summary>
public string? NativeMainSource { get; set; }

/// <summary>
/// Produce optimized binaries (e.g. use -O2 in AOT)
/// </summary>
public bool Optimized { 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 *.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")
Copy link
Contributor

Choose a reason for hiding this comment

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

I still think this should come as input to the task (e.g. from publish)

Copy link
Member Author

@EgorBo EgorBo Apr 13, 2020

Choose a reason for hiding this comment

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

the AppDir is basically the dotnet publish location - we need everything in the directory anyway.
Collection of files as an input makes it harder to use via command line 🙁

.Where(f => !excludes.Contains(Path.GetFileName(f)))
.ToArray();

string binDir = Path.Combine(AppDir, $"bin-{ProjectName}-{Arch}");
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, 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, 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/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>
39 changes: 39 additions & 0 deletions src/mono/AppleAppBuilder/Templates/CMakeLists.txt.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.14.5)

project(%ProjectName%)

set(APP_RESOURCES
%AppResources%
)

add_executable(
%ProjectName%
%MainSource%
runtime.h
runtime.m
modules.m
${APP_RESOURCES}
)

include_directories("%MonoInclude%")

set_target_properties(%ProjectName% PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING "NO"
RESOURCE "${APP_RESOURCES}"
)

# FIXME: `XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING` should not be NO

target_link_libraries(
%ProjectName%
"-framework Foundation"
"-framework Security"
"-framework UIKit"
"-framework GSS"
"-lz"
"-liconv"
%NativeLibrariesToLink%
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<key>CFBundleDevelopmentRegion</key>
<string>en-US</string>
<key>CFBundleExecutable</key>
<string>HelloiOS</string>
<string>%BundleIdentifier%</string>
<key>CFBundleIdentifier</key>
<string>net.dot.HelloiOS</string>
<string>net.dot.%BundleIdentifier%</string>
<key>CFBundleName</key>
<string>%BundleIdentifier%</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>HelloiOS</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
Expand Down
Loading