[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:
Rolf Bjarne Kvinge 2019-08-28 04:54:26 -07:00 коммит произвёл GitHub
Родитель 37e710c44b
Коммит b8e9c83ce0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 197 добавлений и 27 удалений

Просмотреть файл

@ -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;
}