[mtouch] Detect when we run into the 32-bit arm size limitation, and report a better error. Fixes #6526. (#6855)
Also limit the output from the native compiler, so that we don't overload the IDEs with output if the native compiler produces tens of thousands of errors. Fixes https://github.com/xamarin/xamarin-macios/issues/6526.
This commit is contained in:
Родитель
37e710c44b
Коммит
b8e9c83ce0
|
@ -631,6 +631,9 @@ If this is not the case, please file a [bug report](https://github.com/xamarin/x
|
|||
|
||||
<a name="MM5218" />
|
||||
|
||||
<!-- 5107 is used by mtouch-->
|
||||
<!-- 5108 is used by mtouch-->
|
||||
|
||||
#### MM5218: Can't ignore the dynamic symbol {symbol} (--ignore-dynamic-symbol={symbol}) because it was not detected as a dynamic symbol.
|
||||
|
||||
See the [equivalent mtouch warning](~/ios/troubleshooting/mtouch-errors.md#MT5218).
|
||||
|
|
|
@ -2587,6 +2587,46 @@ Reference: https://github.com/xamarin/xamarin-macios/issues/6492
|
|||
|
||||
This usually indicates a bug in Xamarin.iOS; please file a new issue on [github](https://github.com/xamarin/xamarin-macios/issues/new).
|
||||
|
||||
### MT5107: The assembly {assembly} can't be AOT-compiled for 32-bit architectures because the native code is too big for the 32-bit ARM architecture.
|
||||
|
||||
The object file format for 32-bit ARM architectures (armv7 and armv7s) has
|
||||
certain size restrictions for the native code. This error will be shown when
|
||||
the limit is hit.
|
||||
|
||||
Possible solutions:
|
||||
|
||||
* Only build for ARM64. This can be changed in the project's iOS Build
|
||||
options, and it's by far the easiest solution. Apple is on its way to
|
||||
removing the 32-bit ARM architectures, so this will eventually be the
|
||||
final solution too.
|
||||
* Enable the linker for all assemblies (or review any custom linker
|
||||
arguments, such as linker definition files, to avoid skipping the
|
||||
linker). The idea is for the linker to remove more unused code, and if
|
||||
enough unused code is removed, then the size limit won't be reached.
|
||||
* Disable debugging. Debugging support makes the AOT compiler produce more
|
||||
code, which can be enough to hit the limit. This can be changed in the
|
||||
project's iOS Debug options (but the downside is of course that
|
||||
debugging won't work).
|
||||
* Enable LLVM. The LLVM compiler can sometimes produce smaller code, which
|
||||
can be enough to get below the size limit. The downside is that
|
||||
debugging won't work, and the build will be much slower.
|
||||
* There's usually a particularly big assembly that's causing problems;
|
||||
sometimes it can help to split such assemblies into smaller assemblies.
|
||||
This can be a significant amount of work though, and there's no
|
||||
guarantee it would eventually work.
|
||||
* Remove code from the app that isn't needed/used.
|
||||
|
||||
Reference: https://github.com/xamarin/xamarin-macios/issues/6787
|
||||
|
||||
### MT5108: The {linker/compiler} output is too long, it's been limited to 1000 lines.
|
||||
|
||||
This is a warning indicating that the output from the linker or compiler was
|
||||
truncated because it was too long.
|
||||
|
||||
To get the complete output, you can either manually run the command from the
|
||||
command line, or alternatively add `-v -v -v -v -v -v` (six or more -v's) to
|
||||
the additional mtouch argument in the project's iOS Build options.
|
||||
|
||||
### MT52xx: Linking
|
||||
|
||||
<!--
|
||||
|
|
|
@ -3009,6 +3009,30 @@ class TestClass {
|
|||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MT5107 ()
|
||||
{
|
||||
AssertDeviceAvailable ();
|
||||
|
||||
using (var mtouch = new MTouchTool ()) {
|
||||
mtouch.TargetVer = "10.3";
|
||||
mtouch.Profile = Profile.iOS;
|
||||
mtouch.Abi = "armv7";
|
||||
mtouch.Linker = MTouchLinker.DontLink;
|
||||
/* Once the xcode11 branch has been merged into master, we should be able to do the following instead, which will make the test faster
|
||||
mtouch.Linker = MTouchLinker.LinkSdk;
|
||||
mtouch.CustomArguments = new string [] { "--linkskip=System.Core" };
|
||||
mtouch.CreateTemporaryApp (extraCode: "[Foundation.Preserve] class PreserveMe { void M () { System.Console.WriteLine (typeof (System.Collections.Generic.HashSet<string>)); } }", extraArg: "-r:System.Core.dll");
|
||||
*/
|
||||
mtouch.CreateTemporaryApp ();
|
||||
mtouch.AssertExecuteFailure (MTouchAction.BuildDev);
|
||||
mtouch.AssertError (5107, "The assembly 'Xamarin.iOS.dll' can't be AOT-compiled for 32-bit architectures because the native code is too big for the 32-bit ARM architecture.");
|
||||
mtouch.AssertWarning (5108, "The compiler output is too long, it's been limited to 1000 lines.");
|
||||
mtouch.AssertErrorCount (1);
|
||||
mtouch.AssertWarningCount (1);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MT5211 ()
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -293,6 +294,40 @@ namespace Xamarin.Bundler
|
|||
{
|
||||
return GetType ().Name;
|
||||
}
|
||||
|
||||
bool reported_5107;
|
||||
protected void CheckFor5107 (string assembly_name, string line, List<Exception> exceptions)
|
||||
{
|
||||
if (line.Contains ("can not encode offset") && line.Contains ("in resulting scattered relocation")) {
|
||||
if (!reported_5107) {
|
||||
// There can be thousands of these, but we only need one.
|
||||
reported_5107 = true;
|
||||
exceptions.Add (ErrorHelper.CreateError (5107, "The assembly '{0}' can't be AOT-compiled for 32-bit architectures because the native code is too big for the 32-bit ARM architecture.", assembly_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Writes a list of lines to stderr, writing only a limited number of lines if there are too many of them.
|
||||
protected void WriteLimitedOutput (string first, IEnumerable<string> lines, List<Exception> exceptions)
|
||||
{
|
||||
if ((first == null || first.Length == 0) && !lines.Any ())
|
||||
return;
|
||||
|
||||
if (Driver.Verbosity < 6 && lines.Count () > 1000) {
|
||||
lines = lines.Take (1000); // Limit the output so that we don't overload VSfM.
|
||||
exceptions.Add (ErrorHelper.CreateWarning (5108, "The compiler output is too long, it's been limited to 1000 lines."));
|
||||
}
|
||||
|
||||
// Construct the entire message before writing anything, so that there's a better chance the message isn't
|
||||
// mixed up with output from other threads.
|
||||
var sb = new StringBuilder ();
|
||||
if (first != null && first.Length > 0)
|
||||
sb.AppendLine (first);
|
||||
foreach (var line in lines)
|
||||
sb.AppendLine (line);
|
||||
sb.Length -= Environment.NewLine.Length; // strip off the last newline, since we're adding it in the next line
|
||||
Console.Error.WriteLine (sb);
|
||||
}
|
||||
}
|
||||
|
||||
class SingleThreadedSynchronizationContext : SynchronizationContext
|
||||
|
|
|
@ -223,7 +223,19 @@ namespace Xamarin.Bundler {
|
|||
#endif
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, string args, string[] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false)
|
||||
public static int RunCommand (string path, string args)
|
||||
{
|
||||
return RunCommand (path, args, null, (Action<string>) null);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, string args, string [] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
if (output != null)
|
||||
return RunCommand (path, args, env, (v) => { if (v != null) output.AppendLine (v); }, suppressPrintOnErrors);
|
||||
return RunCommand (path, args, env, (Action<string>) null, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, string args, string[] env = null, Action<string> output_received = null, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
Exception stdin_exc = null;
|
||||
var info = new ProcessStartInfo (path, args);
|
||||
|
@ -234,8 +246,15 @@ namespace Xamarin.Bundler {
|
|||
System.Threading.ManualResetEvent stdout_completed = new System.Threading.ManualResetEvent (false);
|
||||
System.Threading.ManualResetEvent stderr_completed = new System.Threading.ManualResetEvent (false);
|
||||
|
||||
if (output == null)
|
||||
var lockobj = new object ();
|
||||
StringBuilder output = null;
|
||||
if (output_received == null) {
|
||||
output = new StringBuilder ();
|
||||
output_received = (line) => {
|
||||
if (line != null)
|
||||
output.AppendLine (line);
|
||||
};
|
||||
}
|
||||
|
||||
if (env != null){
|
||||
if (env.Length % 2 != 0)
|
||||
|
@ -252,8 +271,8 @@ namespace Xamarin.Bundler {
|
|||
|
||||
p.OutputDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (output)
|
||||
output.AppendLine (e.Data);
|
||||
lock (lockobj)
|
||||
output_received (e.Data);
|
||||
} else {
|
||||
stdout_completed.Set ();
|
||||
}
|
||||
|
@ -261,8 +280,8 @@ namespace Xamarin.Bundler {
|
|||
|
||||
p.ErrorDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (output)
|
||||
output.AppendLine (e.Data);
|
||||
lock (lockobj)
|
||||
output_received (e.Data);
|
||||
} else {
|
||||
stderr_completed.Set ();
|
||||
}
|
||||
|
@ -276,13 +295,22 @@ namespace Xamarin.Bundler {
|
|||
stderr_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
stdout_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
|
||||
output_received (null);
|
||||
|
||||
if (p.ExitCode != 0) {
|
||||
// note: this repeat the failing command line. However we can't avoid this since we're often
|
||||
// running commands in parallel (so the last one printed might not be the one failing)
|
||||
if (!suppressPrintOnErrors)
|
||||
Console.Error.WriteLine ("Process exited with code {0}, command:\n{1} {2}{3}", p.ExitCode, path, args, output.Length > 0 ? "\n" + output.ToString () : string.Empty);
|
||||
if (!suppressPrintOnErrors) {
|
||||
// We re-use the stringbuilder so that we avoid duplicating the amount of required memory,
|
||||
// while only calling Console.WriteLine once to make it less probable that other threads
|
||||
// also write to the Console, confusing the output.
|
||||
if (output == null)
|
||||
output = new StringBuilder ();
|
||||
output.Insert (0, $"Process exited with code {p.ExitCode}, command:\n{path}\n");
|
||||
Console.Error.WriteLine (output);
|
||||
}
|
||||
return p.ExitCode;
|
||||
} else if (verbose > 0 && output.Length > 0 && !suppressPrintOnErrors) {
|
||||
} else if (verbose > 0 && output != null && output.Length > 0 && !suppressPrintOnErrors) {
|
||||
Console.WriteLine (output.ToString ());
|
||||
}
|
||||
|
||||
|
@ -295,7 +323,14 @@ namespace Xamarin.Bundler {
|
|||
|
||||
public static Task<int> RunCommandAsync (string path, string args, string [] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
return Task.Run (() => RunCommand (path, args, env, output, suppressPrintOnErrors));
|
||||
if (output != null)
|
||||
return RunCommandAsync (path, args, env, (v) => { if (v != null) output.AppendLine (v); }, suppressPrintOnErrors);
|
||||
return RunCommandAsync (path, args, env, (Action<string>) null, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
public static Task<int> RunCommandAsync (string path, string args, string [] env = null, Action<string> output_received = null, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
return Task.Run (() => RunCommand (path, args, env, output_received, suppressPrintOnErrors));
|
||||
}
|
||||
|
||||
#if !MMP_TEST
|
||||
|
|
|
@ -37,6 +37,15 @@ namespace Xamarin.Bundler
|
|||
return Task.Run (() => Start ());
|
||||
}
|
||||
|
||||
// both stdout and stderr will be sent to this function.
|
||||
// null will be sent when there's no more output
|
||||
// calls to this function will be synchronized (no need to lock in here).
|
||||
protected virtual void OutputReceived (string line)
|
||||
{
|
||||
if (line != null)
|
||||
Output.AppendLine (line);
|
||||
}
|
||||
|
||||
protected int Start ()
|
||||
{
|
||||
if (Driver.Verbosity > 0)
|
||||
|
@ -45,6 +54,7 @@ namespace Xamarin.Bundler
|
|||
var info = ProcessStartInfo;
|
||||
var stdout_completed = new ManualResetEvent (false);
|
||||
var stderr_completed = new ManualResetEvent (false);
|
||||
var lockobj = new object ();
|
||||
|
||||
Output = new StringBuilder ();
|
||||
|
||||
|
@ -52,8 +62,8 @@ namespace Xamarin.Bundler
|
|||
p.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data != null) {
|
||||
lock (Output)
|
||||
Output.AppendLine (e.Data);
|
||||
lock (lockobj)
|
||||
OutputReceived (e.Data);
|
||||
} else {
|
||||
stdout_completed.Set ();
|
||||
}
|
||||
|
@ -62,8 +72,8 @@ namespace Xamarin.Bundler
|
|||
p.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data != null) {
|
||||
lock (Output)
|
||||
Output.AppendLine (e.Data);
|
||||
lock (lockobj)
|
||||
OutputReceived (e.Data);
|
||||
} else {
|
||||
stderr_completed.Set ();
|
||||
}
|
||||
|
@ -77,6 +87,8 @@ namespace Xamarin.Bundler
|
|||
stderr_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
stdout_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
|
||||
OutputReceived (null);
|
||||
|
||||
GC.Collect (); // Workaround for: https://bugzilla.xamarin.com/show_bug.cgi?id=43462#c14
|
||||
|
||||
if (Driver.Verbosity >= 2 && Output.Length > 0)
|
||||
|
@ -181,6 +193,8 @@ namespace Xamarin.Bundler
|
|||
public string AssemblyPath; // path to the .s file.
|
||||
List<string> inputs;
|
||||
public AotInfo AotInfo;
|
||||
List<Exception> exceptions = new List<Exception> ();
|
||||
List<string> output_lines = new List<string> ();
|
||||
|
||||
public override IEnumerable<string> Outputs {
|
||||
get {
|
||||
|
@ -226,6 +240,19 @@ namespace Xamarin.Bundler
|
|||
}
|
||||
}
|
||||
|
||||
protected override void OutputReceived (string line)
|
||||
{
|
||||
if (line == null)
|
||||
return;
|
||||
|
||||
if (line.StartsWith ("AOT restriction: Method '", StringComparison.Ordinal) && line.Contains ("must be static since it is decorated with [MonoPInvokeCallback]")) {
|
||||
exceptions.Add (ErrorHelper.CreateError (3002, line));
|
||||
} else {
|
||||
CheckFor5107 (AssemblyName, line, exceptions);
|
||||
}
|
||||
output_lines.Add (line);
|
||||
}
|
||||
|
||||
protected async override Task ExecuteAsync ()
|
||||
{
|
||||
var exit_code = await StartAsync ();
|
||||
|
@ -241,19 +268,11 @@ namespace Xamarin.Bundler
|
|||
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 ());
|
||||
}
|
||||
WriteLimitedOutput ($"AOT Compilation exited with code {exit_code}, command:\n{Command}", output_lines, exceptions);
|
||||
|
||||
throw new MonoTouchException (3001, true, "Could not AOT the assembly '{0}'", AssemblyName);
|
||||
exceptions.Add (ErrorHelper.CreateError (3001, "Could not AOT the assembly '{0}'", AssemblyName));
|
||||
|
||||
throw new AggregateException (exceptions);
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
|
@ -545,7 +564,21 @@ namespace Xamarin.Bundler
|
|||
|
||||
Directory.CreateDirectory (Path.GetDirectoryName (OutputFile));
|
||||
|
||||
var rv = await Driver.RunCommandAsync (App.CompilerPath, CompilerFlags.ToString (), null, null);
|
||||
var exceptions = new List<Exception> ();
|
||||
var output = new List<string> ();
|
||||
var assembly_name = Path.GetFileNameWithoutExtension (OutputFile);
|
||||
var output_received = new Action<string> ((string line) => {
|
||||
if (line == null)
|
||||
return;
|
||||
output.Add (line);
|
||||
CheckFor5107 (assembly_name, line, exceptions);
|
||||
});
|
||||
|
||||
var rv = await Driver.RunCommandAsync (App.CompilerPath, CompilerFlags.ToString (), null, output_received, suppressPrintOnErrors: true);
|
||||
|
||||
WriteLimitedOutput (rv != 0 ? $"Compilation failed with code {rv}, command:\n{App.CompilerPath} {CompilerFlags.ToString ()}" : null, output, exceptions);
|
||||
|
||||
ErrorHelper.Show (exceptions);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче