xamarin-macios/msbuild/Xamarin.MacDev.Tasks/Tasks/AOTCompileTaskBase.cs

130 строки
4.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Xamarin.Localization.MSBuild;
using Xamarin.Utils;
#nullable enable
namespace Xamarin.MacDev.Tasks {
public abstract class AOTCompileTaskBase : XamarinTask {
public ITaskItem [] AotArguments { get; set; } = Array.Empty<ITaskItem> ();
[Required]
public string AOTCompilerPath { get; set; } = string.Empty;
[Required]
public ITaskItem [] Assemblies { get; set; } = Array.Empty<ITaskItem> ();
[Required]
public string InputDirectory { get; set; } = string.Empty;
[Required]
public string MinimumOSVersion { get; set; } = string.Empty;
[Required]
public string OutputDirectory { get; set; } = string.Empty;
[Required]
public string SdkDevPath { get; set; } = string.Empty;
#region Output
[Output]
public ITaskItem []? AssemblyFiles { get; set; }
[Output]
public ITaskItem []? FileWrites { get; set; }
#endregion
public override bool Execute ()
{
var inputs = new List<string> (Assemblies.Length);
for (var i = 0; i < Assemblies.Length; i++) {
inputs.Add (Path.GetFullPath (Assemblies [i].ItemSpec));
}
// All the assemblies to AOT must be in the same directory
var assemblyDirectories = inputs.Select (v => Path.GetDirectoryName (Path.GetFullPath (v))).Distinct ().ToArray ();
if (assemblyDirectories.Length > 1) {
// The assemblies are not in the same directory, so copy them somewhere else (to InputDirectory)
Directory.CreateDirectory (InputDirectory);
for (var i = 0; i < inputs.Count; i++) {
var newInput = Path.Combine (InputDirectory, Path.GetFileName (inputs [i]));
File.Copy (inputs [i], newInput, true);
inputs [i] = newInput;
}
} else {
// The assemblies are all in the same directory, we can just use that as input.
InputDirectory = assemblyDirectories [0];
}
Directory.CreateDirectory (OutputDirectory);
var aotAssemblyFiles = new List<ITaskItem> ();
var processes = new Task<Execution> [Assemblies.Length];
var environment = new Dictionary<string, string> {
{ "MONO_PATH", Path.GetFullPath (InputDirectory) },
};
var globalAotArguments = AotArguments?.Select (v => v.ItemSpec).ToList ();
for (var i = 0; i < Assemblies.Length; i++) {
var asm = Assemblies [i];
var input = inputs [i];
var arch = Assemblies [i].GetMetadata ("Arch");
var aotArguments = Assemblies [i].GetMetadata ("Arguments");
var processArguments = Assemblies [i].GetMetadata ("ProcessArguments");
var aotData = Assemblies [i].GetMetadata ("AOTData");
var aotAssembly = Assemblies [i].GetMetadata ("AOTAssembly");
var aotAssemblyItem = new TaskItem (aotAssembly);
aotAssemblyItem.SetMetadata ("Arguments", "-Xlinker -rpath -Xlinker @executable_path/ -Qunused-arguments -x assembler -D DEBUG");
aotAssemblyItem.SetMetadata ("Arch", arch);
aotAssemblyFiles.Add (aotAssemblyItem);
var arguments = new List<string> ();
if (!StringUtils.TryParseArguments (aotArguments, out var parsedArguments, out var ex)) {
Log.LogError (MSBStrings.E7071, /* Unable to parse the AOT compiler arguments: {0} ({1}) */ aotArguments, ex!.Message);
return false;
}
if (!StringUtils.TryParseArguments (processArguments, out var parsedProcessArguments, out var ex2)) {
Log.LogError (MSBStrings.E7071, /* Unable to parse the AOT compiler arguments: {0} ({1}) */ processArguments, ex2!.Message);
return false;
}
arguments.Add ($"{string.Join (",", parsedArguments)}");
if (globalAotArguments is not null)
arguments.Add ($"--aot={string.Join (",", globalAotArguments)}");
arguments.AddRange (parsedProcessArguments);
arguments.Add (input);
processes [i] = ExecuteAsync (AOTCompilerPath, arguments, environment: environment, sdkDevPath: SdkDevPath, showErrorIfFailure: false /* we show our own error below */)
.ContinueWith ((v) => {
if (v.Result.ExitCode != 0)
Log.LogError ("Failed to AOT compile {0}, the AOT compiler exited with code {1}", Path.GetFileName (input), v.Result.ExitCode);
return System.Threading.Tasks.Task.FromResult<Execution> (v.Result);
}).Unwrap ();
}
System.Threading.Tasks.Task.WaitAll (processes);
AssemblyFiles = aotAssemblyFiles.ToArray ();
// For Windows support it's necessary to have the files we're going to create as an Output parameter, so that the files are
// created on the windows side too, which makes the Inputs/Outputs logic work properly when working from Windows.
var objectFiles = Assemblies.Select (v => v.GetMetadata ("ObjectFile")).Where (v => !string.IsNullOrEmpty (v));
var llvmFiles = Assemblies.Select (v => v.GetMetadata ("LLVMFile")).Where (v => !string.IsNullOrEmpty (v));
FileWrites = objectFiles.Union (llvmFiles).Select (v => new TaskItem (v)).ToArray ();
return !Log.HasLoggedErrors;
}
}
}