mirror of https://github.com/dotnet/runtime
235 lines
7.3 KiB
C#
235 lines
7.3 KiB
C#
// 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.Text;
|
|
using System.Linq;
|
|
using Microsoft.Build.Framework;
|
|
using Microsoft.Build.Utilities;
|
|
|
|
public class AppleAppBuilderTask : Task
|
|
{
|
|
/// <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 MonoRuntimeHeaders { get; set; } = ""!;
|
|
|
|
/// <summary>
|
|
/// This library will be used as an entry-point (e.g. TestRunner.dll)
|
|
/// </summary>
|
|
[Required]
|
|
public string MainLibraryFileName { get; set; } = ""!;
|
|
|
|
/// <summary>
|
|
/// List of paths to assemblies to be included in the app. For AOT builds the 'ObjectFile' metadata key needs to point to the object file.
|
|
/// </summary>
|
|
[Required]
|
|
public ITaskItem[] Assemblies { get; set; } = Array.Empty<ITaskItem>();
|
|
|
|
/// <summary>
|
|
/// Path to store build artifacts
|
|
/// </summary>
|
|
public string? OutputDirectory { get; set; }
|
|
|
|
/// <summary>
|
|
/// Produce optimized binaries and use 'Release' config in xcode
|
|
/// </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 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 (!File.Exists(Path.Combine(AppDir, MainLibraryFileName)))
|
|
{
|
|
throw new ArgumentException($"MainLibraryFileName='{MainLibraryFileName}' was not found in AppDir='{AppDir}'");
|
|
}
|
|
|
|
if (ProjectName.Contains(" "))
|
|
{
|
|
throw new ArgumentException($"ProjectName='{ProjectName}' should not contain spaces");
|
|
}
|
|
|
|
string[] excludes = new string[0];
|
|
if (ExcludeFromAppDir != null)
|
|
{
|
|
excludes = ExcludeFromAppDir
|
|
.Where(i => !string.IsNullOrEmpty(i.ItemSpec))
|
|
.Select(i => i.ItemSpec)
|
|
.ToArray();
|
|
}
|
|
|
|
string binDir = Path.Combine(AppDir, $"bin-{ProjectName}-{Arch}");
|
|
if (!string.IsNullOrEmpty(OutputDirectory))
|
|
{
|
|
binDir = OutputDirectory;
|
|
}
|
|
Directory.CreateDirectory(binDir);
|
|
|
|
var assemblerFiles = new List<string>();
|
|
foreach (ITaskItem file in Assemblies)
|
|
{
|
|
// use AOT files if available
|
|
var obj = file.GetMetadata("AssemblerFile");
|
|
if (!String.IsNullOrEmpty(obj))
|
|
{
|
|
assemblerFiles.Add(obj);
|
|
}
|
|
}
|
|
|
|
if (isDevice && !assemblerFiles.Any())
|
|
{
|
|
throw new InvalidOperationException("Need list of AOT files for device builds.");
|
|
}
|
|
|
|
// generate modules.m
|
|
GenerateLinkAllFile(
|
|
assemblerFiles,
|
|
Path.Combine(binDir, "modules.m"));
|
|
|
|
if (GenerateXcodeProject)
|
|
{
|
|
XcodeProjectPath = Xcode.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles,
|
|
AppDir, binDir, MonoRuntimeHeaders, !isDevice, 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void GenerateLinkAllFile(IEnumerable<string> asmFiles, 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 asmFile in asmFiles)
|
|
{
|
|
string symbol = "mono_aot_module_" +
|
|
Path.GetFileName(asmFile)
|
|
.Replace(".dll.s", "")
|
|
.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}.");
|
|
}
|
|
}
|