xamarin-macios/tools/mtouch/BuildTasks.mtouch.cs

592 строки
18 KiB
C#
Исходник Обычный вид История

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
using System.Threading.Tasks;
using Xamarin.MacDev;
using Xamarin.Utils;
namespace Xamarin.Bundler
{
public abstract class ProcessTask : BuildTask
{
public ProcessStartInfo ProcessStartInfo;
protected StringBuilder Output;
protected string Command {
get {
var result = new StringBuilder ();
if (ProcessStartInfo.EnvironmentVariables.ContainsKey ("MONO_PATH")) {
result.Append ("MONO_PATH=");
result.Append (StringUtils.Quote (ProcessStartInfo.EnvironmentVariables ["MONO_PATH"]));
result.Append (' ');
}
result.Append (ProcessStartInfo.FileName);
result.Append (' ');
result.Append (ProcessStartInfo.Arguments);
return result.ToString ();
}
}
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
protected Task<int> StartAsync ()
{
return Task.Run (() => Start ());
}
protected int Start ()
{
if (Driver.Verbosity > 0)
Console.WriteLine (Command);
var info = ProcessStartInfo;
var stdout_completed = new ManualResetEvent (false);
var stderr_completed = new ManualResetEvent (false);
Output = new StringBuilder ();
using (var p = Process.Start (info)) {
p.OutputDataReceived += (sender, e) =>
{
if (e.Data != null) {
lock (Output)
Output.AppendLine (e.Data);
} else {
stdout_completed.Set ();
}
};
p.ErrorDataReceived += (sender, e) =>
{
if (e.Data != null) {
lock (Output)
Output.AppendLine (e.Data);
} else {
stderr_completed.Set ();
}
};
p.BeginOutputReadLine ();
p.BeginErrorReadLine ();
p.WaitForExit ();
stderr_completed.WaitOne (TimeSpan.FromSeconds (1));
stdout_completed.WaitOne (TimeSpan.FromSeconds (1));
GC.Collect (); // Workaround for: https://bugzilla.xamarin.com/show_bug.cgi?id=43462#c14
if (Driver.Verbosity >= 2 && Output.Length > 0)
Console.Error.WriteLine (Output.ToString ());
return p.ExitCode;
}
}
}
class GenerateMainTask : BuildTask
{
public Target Target;
public Abi Abi;
public string MainM;
public IList<string> RegistrationMethods;
public override IEnumerable<string> Inputs {
get {
foreach (var asm in Target.Assemblies)
yield return asm.FullPath;
}
}
public override IEnumerable<string> Outputs {
get {
yield return MainM;
}
}
protected override void Execute ()
{
Driver.GenerateMain (Target.App, Target.Assemblies, Target.App.AssemblyName, Abi, MainM, RegistrationMethods);
}
}
class CompileMainTask : CompileTask
{
protected override void CompilationFailed (int exitCode)
{
throw ErrorHelper.CreateError (5103, "Failed to compile the file(s) '{0}'. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new", string.Join ("', '", CompilerFlags.SourceFiles.ToArray ()));
}
}
class PinvokesTask : CompileTask
{
protected override void CompilationFailed (int exitCode)
{
throw ErrorHelper.CreateError (4002, "Failed to compile the generated code for P/Invoke methods. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new");
}
}
2017-01-25 14:06:42 +03:00
class RunRegistrarTask : BuildTask
{
public Target Target;
public string RegistrarCodePath;
public string RegistrarHeaderPath;
2017-01-25 14:06:42 +03:00
public override IEnumerable<string> Inputs {
get {
foreach (var asm in Target.Assemblies)
yield return asm.FullPath;
}
}
public override IEnumerable<string> Outputs {
get {
yield return RegistrarHeaderPath;
yield return RegistrarCodePath;
}
}
protected override void Execute ()
2017-01-25 14:06:42 +03:00
{
Target.StaticRegistrar.Generate (Target.Assemblies.Select ((a) => a.AssemblyDefinition), RegistrarHeaderPath, RegistrarCodePath);
2017-01-25 14:06:42 +03:00
}
}
class CompileRegistrarTask : CompileTask
{
public string RegistrarCodePath;
public string RegistrarHeaderPath;
public override IEnumerable<string> Inputs {
get {
yield return RegistrarHeaderPath;
yield return RegistrarCodePath;
}
}
protected override void CompilationFailed (int exitCode)
{
throw ErrorHelper.CreateError (4109, "Failed to compile the generated registrar code. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new");
}
}
public class AOTTask : ProcessTask
{
public Assembly Assembly;
public string AssemblyName;
public bool AddBitcodeMarkerSection;
public string AssemblyPath; // path to the .s file.
List<string> inputs;
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
public AotInfo AotInfo;
public override IEnumerable<string> Outputs {
get {
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
return AotInfo.AotDataFiles
.Union (AotInfo.AsmFiles)
.Union (AotInfo.BitcodeFiles)
.Union (AotInfo.ObjectFiles);
}
}
public override IEnumerable<string> Inputs {
get {
yield return Assembly.FullPath;
}
}
public override IEnumerable<string> FileDependencies {
get {
if (inputs == null) {
inputs = new List<string> ();
if (Assembly.HasDependencyMap)
inputs.AddRange (Assembly.DependencyMap);
inputs.Add (AssemblyName);
inputs.Add (Driver.GetAotCompiler (Assembly.App, Assembly.Target.Is64Build));
var mdb = Assembly.FullPath + ".mdb";
if (File.Exists (mdb))
inputs.Add (mdb);
var pdb = Path.ChangeExtension (Assembly.FullPath, ".pdb");
if (File.Exists (pdb))
inputs.Add (pdb);
var config = Assembly.FullPath + ".config";
if (File.Exists (config))
inputs.Add (config);
}
return inputs;
}
}
public override bool IsUptodate {
get {
// We can only check dependencies if we know the assemblies this assembly depend on (otherwise always rebuild).
return Assembly.HasDependencyMap && base.IsUptodate;
}
}
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
protected async override Task ExecuteAsync ()
{
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
var exit_code = await StartAsync ();
if (exit_code == 0) {
if (AddBitcodeMarkerSection)
File.AppendAllText (AssemblyPath, @"
.section __LLVM, __bitcode
.byte 0
.section __LLVM, __cmdline
.byte 0
");
return;
}
Console.Error.WriteLine ("AOT Compilation exited with code {0}, command:\n{1}{2}", exit_code, Command, Output.Length > 0 ? ("\n" + Output.ToString ()) : string.Empty);
if (Output.Length > 0) {
List<Exception> exceptions = new List<Exception> ();
foreach (var line in Output.ToString ().Split ('\n')) {
if (line.StartsWith ("AOT restriction: Method '", StringComparison.Ordinal) && line.Contains ("must be static since it is decorated with [MonoPInvokeCallback]")) {
exceptions.Add (new MonoTouchException (3002, true, line));
}
}
if (exceptions.Count > 0)
throw new AggregateException (exceptions.ToArray ());
}
throw new MonoTouchException (3001, true, "Could not AOT the assembly '{0}'", AssemblyName);
}
public override string ToString ()
{
return Path.GetFileName (AssemblyName);
}
}
public class NativeLinkTask : BuildTask
{
public Target Target;
public string OutputFile;
public CompilerFlags CompilerFlags;
public override IEnumerable<string> Inputs {
get {
CompilerFlags.PopulateInputs ();
return CompilerFlags.Inputs;
}
}
public override IEnumerable<string> Outputs {
get {
yield return OutputFile;
}
}
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
protected override async Task ExecuteAsync ()
{
// always show the native linker warnings since many of them turn out to be very important
// and very hard to diagnose otherwise when hidden from the build output. Ref: bug #2430
var linker_errors = new List<Exception> ();
var output = new StringBuilder ();
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
var code = await Driver.RunCommandAsync (Target.App.CompilerPath, CompilerFlags.ToString (), null, output);
Application.ProcessNativeLinkerOutput (Target, output.ToString (), CompilerFlags.AllLibraries, linker_errors, code != 0);
if (code != 0) {
// if the build failed - it could be because of missing frameworks / libraries we identified earlier
foreach (var assembly in Target.Assemblies) {
if (assembly.UnresolvedModuleReferences == null)
continue;
foreach (var mr in assembly.UnresolvedModuleReferences) {
// TODO: add more diagnose information on the warnings
var name = Path.GetFileNameWithoutExtension (mr.Name);
linker_errors.Add (new MonoTouchException (5215, false, "References to '{0}' might require additional -framework=XXX or -lXXX instructions to the native linker", name));
}
}
// mtouch does not validate extra parameters given to GCC when linking (--gcc_flags)
if (!String.IsNullOrEmpty (Target.App.UserGccFlags))
linker_errors.Add (new MonoTouchException (5201, true, "Native linking failed. Please review the build log and the user flags provided to gcc: {0}", Target.App.UserGccFlags));
linker_errors.Add (new MonoTouchException (5202, true, "Native linking failed. Please review the build log.", Target.App.UserGccFlags));
[mtouch] Improve how we make sure native symbols aren't stripped away. Fixes #51710 and #54417. (#2162) * [mtouch] Improve how we make sure native symbols aren't stripped away. Fixes #51710 and #54417. * Refactor required symbol collection to store more information about each symbol (field, function, Objective-C class), and in general make the code more straight forward. * Implement support for generating source code that references these symbols, and do this whenever we can't ask the native linker to keep these symbols (when using bitcode). Additionally make it possible to do this manually, so that the source code can be generated for non-bitcode platforms too (which is useful if the number of symbols is enormous, in which case we might surpass the maximum command-line length). * Also make it possible to completely ignore native symbols, or ignore them on a per-symbol basis. This provides a fallback for users if we get something right and we try to preserve something that shouldn't be preserved (for instance if it doesn't exist), and the user ends up with unfixable linker errors. * Don't collect Objective-C classes unless they're in an assembly with LinkWith attributes. We don't need to preserve Objective-C classes in any other circumstances. * Implement everything for both Xamarin.iOS and Xamarin.Mac, and share the code between them. * Remove previous workaround for bug #51710, since it's no longer needed. * Add tests. https://bugzilla.xamarin.com/show_bug.cgi?id=54417 https://bugzilla.xamarin.com/show_bug.cgi?id=51710 * [mtouch] Make sure to only keep symbols from the current app when code sharing. This fixes a build problem with the interdependent-binding-projects test when testing in Today Extension mode.
2017-06-02 19:29:19 +03:00
if (code == 255) {
// check command length
// getconf ARG_MAX
StringBuilder getconf_output = new StringBuilder ();
if (Driver.RunCommand ("getconf", "ARG_MAX", output: getconf_output, suppressPrintOnErrors: true) == 0) {
int arg_max;
if (int.TryParse (getconf_output.ToString ().Trim (' ', '\t', '\n', '\r'), out arg_max)) {
var cmd_length = Target.App.CompilerPath.Length + 1 + CompilerFlags.ToString ().Length;
if (cmd_length > arg_max) {
linker_errors.Add (ErrorHelper.CreateWarning (5217, $"Native linking possibly failed because the linker command line was too long ({cmd_length} characters)."));
} else {
Driver.Log (3, $"Linker failure is probably not due to command-line length (actual: {cmd_length} limit: {arg_max}");
}
} else {
Driver.Log (3, "Failed to parse 'getconf ARG_MAX' output: {0}", getconf_output);
}
} else {
Driver.Log (3, "Failed to execute 'getconf ARG_MAX'\n{0}", getconf_output);
}
}
}
ErrorHelper.Show (linker_errors);
// the native linker can prefer private (and existing) over public (but non-existing) framework when weak_framework are used
// on an iOS target version where the framework does not exists, e.g. targeting iOS6 for JavaScriptCore added in iOS7 results in
// /System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore instead of
// /System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore
// more details in https://bugzilla.xamarin.com/show_bug.cgi?id=31036
if (CompilerFlags.WeakFrameworks.Count > 0)
Target.AdjustDylibs (Target.Executable);
Driver.Watch ("Native Link", 1);
}
public override string ToString ()
{
return Path.GetFileName (OutputFile);
}
}
public class LinkTask : CompileTask
{
protected override async Task ExecuteAsync ()
{
await base.ExecuteAsync ();
// we can't trust the native linker to pick the right (non private) framework when an older TargetVersion is used
if (CompilerFlags.WeakFrameworks.Count > 0)
Target.AdjustDylibs (OutputFile);
}
protected override void CompilationFailed (int exitCode)
{
throw ErrorHelper.CreateError (5216, "Native linking failed for '{0}'. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new", OutputFile);
}
}
public class CompileTask : BuildTask
{
public Target Target;
public Application App { get { return Target.App; } }
public bool SharedLibrary;
public string OutputFile;
public Abi Abi;
public string InstallName;
public string Language;
public override IEnumerable<string> Inputs {
get {
CompilerFlags.PopulateInputs ();
return CompilerFlags.Inputs;
}
}
public override IEnumerable<string> Outputs {
get {
yield return OutputFile;
}
}
public bool IsAssembler {
get {
return Language == "assembler";
}
}
public string InputFile {
set {
// This is an accumulative setter-only property,
// to make it possible add dependencies using object initializers.
CompilerFlags.AddSourceFile (value);
}
}
CompilerFlags compiler_flags;
public CompilerFlags CompilerFlags {
get { return compiler_flags ?? (compiler_flags = new CompilerFlags (Target)); }
set { compiler_flags = value; }
}
public static void GetArchFlags (CompilerFlags flags, params Abi [] abis)
{
GetArchFlags (flags, (IEnumerable<Abi>) abis);
}
public static void GetArchFlags (CompilerFlags flags, IEnumerable<Abi> abis)
{
bool enable_thumb = false;
foreach (var abi in abis) {
var arch = abi.AsArchString ();
flags.AddOtherFlag ($"-arch {arch}");
enable_thumb |= (abi & Abi.Thumb) != 0;
}
if (enable_thumb)
flags.AddOtherFlag ("-mthumb");
}
public static void GetCompilerFlags (Application app, CompilerFlags flags, bool is_assembler, string language = null)
{
if (!is_assembler)
flags.AddOtherFlag ("-gdwarf-2");
if (!is_assembler) {
if (string.IsNullOrEmpty (language) || !language.Contains ("++")) {
// error: invalid argument '-std=c99' not allowed with 'C++/ObjC++'
flags.AddOtherFlag ("-std=c99");
}
flags.AddOtherFlag ($"-I{StringUtils.Quote (Path.Combine (Driver.GetProductSdkDirectory (app), "usr", "include"))}");
}
flags.AddOtherFlag ($"-isysroot {StringUtils.Quote (Driver.GetFrameworkDirectory (app))}");
flags.AddOtherFlag ("-Qunused-arguments"); // don't complain about unused arguments (clang reports -std=c99 and -Isomething as unused).
}
public static void GetSimulatorCompilerFlags (CompilerFlags flags, bool is_assembler, Application app, string language = null)
{
GetCompilerFlags (app, flags, is_assembler, language);
string sim_platform = Driver.GetPlatformDirectory (app);
string plist = Path.Combine (sim_platform, "Info.plist");
var dict = Driver.FromPList (plist);
var dp = dict.Get<PDictionary> ("DefaultProperties");
if (dp.GetString ("GCC_OBJC_LEGACY_DISPATCH") == "YES")
flags.AddOtherFlag ("-fobjc-legacy-dispatch");
string objc_abi = dp.GetString ("OBJC_ABI_VERSION");
if (!String.IsNullOrWhiteSpace (objc_abi))
flags.AddOtherFlag ($"-fobjc-abi-version={objc_abi}");
plist = Path.Combine (Driver.GetFrameworkDirectory (app), "SDKSettings.plist");
string min_prefix = app.CompilerPath.Contains ("clang") ? Driver.GetTargetMinSdkName (app) : "iphoneos";
dict = Driver.FromPList (plist);
dp = dict.Get<PDictionary> ("DefaultProperties");
if (app.DeploymentTarget == new Version ()) {
string target = dp.GetString ("IPHONEOS_DEPLOYMENT_TARGET");
if (!String.IsNullOrWhiteSpace (target))
flags.AddOtherFlag ($"-m{min_prefix}-version-min={target}");
} else {
flags.AddOtherFlag ($"-m{min_prefix}-version-min={app.DeploymentTarget}");
}
string defines = dp.GetString ("GCC_PRODUCT_TYPE_PREPROCESSOR_DEFINITIONS");
if (!String.IsNullOrWhiteSpace (defines))
flags.AddDefine (defines.Replace (" ", String.Empty));
}
void GetDeviceCompilerFlags (CompilerFlags flags, bool is_assembler)
{
GetCompilerFlags (App, flags, is_assembler, Language);
flags.AddOtherFlag ($"-m{Driver.GetTargetMinSdkName (App)}-version-min={App.DeploymentTarget.ToString ()}");
}
void GetSharedCompilerFlags (CompilerFlags flags, string install_name)
{
if (string.IsNullOrEmpty (install_name))
throw new ArgumentNullException (nameof (install_name));
flags.AddOtherFlag ("-shared");
if (!App.EnableBitCode && !Target.Is64Build)
flags.AddOtherFlag ("-read_only_relocs suppress");
if (App.EnableBitCode)
flags.AddOtherFlag ("-lc++");
flags.LinkWithMono ();
flags.AddOtherFlag ("-install_name " + StringUtils.Quote (install_name));
flags.AddOtherFlag ("-fapplication-extension"); // fixes this: warning MT5203: Native linking warning: warning: linking against dylib not safe for use in application extensions: [..]/actionextension.dll.arm64.dylib
}
void GetStaticCompilerFlags (CompilerFlags flags)
{
flags.AddOtherFlag ("-c");
}
void GetBitcodeCompilerFlags (CompilerFlags flags)
{
flags.AddOtherFlag (App.EnableMarkerOnlyBitCode ? "-fembed-bitcode-marker" : "-fembed-bitcode");
}
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
protected override async Task ExecuteAsync ()
{
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
int exitCode = await CompileAsync ();
if (exitCode != 0)
CompilationFailed (exitCode);
}
protected virtual void CompilationFailed (int exitCode)
{
throw ErrorHelper.CreateError (5106, "Could not compile the file(s) '{0}'. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new", string.Join ("', '", CompilerFlags.SourceFiles.ToArray ()));
}
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
protected async Task<int> CompileAsync ()
{
if (App.IsDeviceBuild) {
GetDeviceCompilerFlags (CompilerFlags, IsAssembler);
} else {
GetSimulatorCompilerFlags (CompilerFlags, IsAssembler, App, Language);
}
if (App.EnableBitCode)
GetBitcodeCompilerFlags (CompilerFlags);
GetArchFlags (CompilerFlags, Abi);
if (SharedLibrary) {
GetSharedCompilerFlags (CompilerFlags, InstallName);
} else {
GetStaticCompilerFlags (CompilerFlags);
}
if (App.EnableDebug)
CompilerFlags.AddDefine ("DEBUG");
CompilerFlags.AddOtherFlag ($"-o {StringUtils.Quote (OutputFile)}");
if (!string.IsNullOrEmpty (Language))
CompilerFlags.AddOtherFlag ($"-x {Language}");
Directory.CreateDirectory (Path.GetDirectoryName (OutputFile));
[mtouch] Rework how tasks are built. The previous build system kept a forward-pointing single linked list of tasks to execute: task X had a list of subsequent tasks to execute. If task X was up-to-date, it was not created (and the next tasks were directly added to the list of tasks to execute). In this world it became complicated to merge output from tasks (for instance if the output of task X and task Y should be a consumed by a single task producing a single output, since the corresponding task would end up in both X's and Y's list of subsequent tasks). Example: creating a single framework from the aot-compiled output of multiple assemblies. So I've reversed the logic: now we keep track of the final output, and then each task has a list of dependencies that must be built. This makes it trivial to create merging tasks (for the previous example, there could for instance be a CreateFrameworkTask, where its dependencies would be all the corresponding AotTasks). We also always create every task, and then each task decides when its executed whether it should do anything or not. This makes it unnecessary to 'forward- delete' files when creating tasks (say you have three tasks, A, B, C; B depends on A, and C depends on B; if A's output isn't up-to-date, it has to delete its own output if it exists, otherwise B would not detect that it would have to re-execute, because at task *creation* time, B's input hadn't changed). Additionally make it based on async/await, since much of the work happens in externel processes (and we don't need to spin up additional threads just to run external processes). This makes us have less code run on background threads, which makes any issues with thread-safety less likely.
2017-01-26 12:56:55 +03:00
var rv = await Driver.RunCommandAsync (App.CompilerPath, CompilerFlags.ToString (), null, null);
return rv;
}
public override string ToString ()
{
if (compiler_flags == null || compiler_flags.SourceFiles == null)
return Path.GetFileName (OutputFile);
return string.Join (", ", compiler_flags.SourceFiles.Select ((arg) => Path.GetFileName (arg)).ToArray ());
}
}
public class BitCodeifyTask : BuildTask
{
public string Input { get; set; }
public string OutputFile { get; set; }
public ApplePlatform Platform { get; set; }
public Abi Abi { get; set; }
public Version DeploymentTarget { get; set; }
public override IEnumerable<string> Inputs {
get {
yield return Input;
}
}
public override IEnumerable<string> Outputs {
get {
yield return OutputFile;
}
}
protected override void Execute ()
{
new BitcodeConverter (Input, OutputFile, Platform, Abi, DeploymentTarget).Convert ();
}
public override string ToString ()
{
return Path.GetFileName (Input);
}
}
}