374 строки
16 KiB
C#
374 строки
16 KiB
C#
// Copyright 2015 Xamarin Inc. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
using Mono.Cecil;
|
|
using NUnit.Framework;
|
|
|
|
using Xamarin.Tests;
|
|
|
|
namespace Xamarin.Linker {
|
|
public class ProfilePoker : MobileProfile {
|
|
|
|
static ProfilePoker p = new ProfilePoker ();
|
|
|
|
public override string ProductAssembly => throw new NotImplementedException ();
|
|
|
|
public static bool IsWellKnownSdk (string assemblyName)
|
|
{
|
|
return p.IsSdk (assemblyName);
|
|
}
|
|
|
|
protected override bool IsProduct (string assemblyName)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
[TestFixture]
|
|
public partial class SdkTest {
|
|
|
|
static string UnifiedPath { get { return Path.Combine (Configuration.MonoTouchRootDirectory, "lib/mono/Xamarin.iOS/"); } }
|
|
static string tvOSPath { get { return Path.Combine (Configuration.MonoTouchRootDirectory, "lib/mono/Xamarin.TVOS/"); } }
|
|
static string watchOSPath { get { return Path.Combine (Configuration.MonoTouchRootDirectory, "lib/mono/Xamarin.WatchOS/"); } }
|
|
|
|
void BCL (string path)
|
|
{
|
|
var failed_bcl = new List<string> ();
|
|
foreach (var file in Directory.GetFiles (path, "*.dll")) {
|
|
var aname = Path.GetFileNameWithoutExtension (file);
|
|
switch (aname) {
|
|
case "FSharp.Core":
|
|
case "I18N":
|
|
case "I18N.CJK":
|
|
case "I18N.MidEast":
|
|
case "I18N.Other":
|
|
case "I18N.Rare":
|
|
case "I18N.West":
|
|
// need to be sure they are link-capable to include them into MobileProfile
|
|
break;
|
|
case "MonoTouch.Dialog-1":
|
|
case "MonoTouch.NUnitLite":
|
|
case "Xamarin.iOS":
|
|
case "Xamarin.TVOS":
|
|
case "Xamarin.WatchOS":
|
|
// product assembly (use a different check than SDK/BCL)
|
|
break;
|
|
case "Newtonsoft.Json":
|
|
case "Xamarin.iOS.Tasks":
|
|
case "Xamarin.ObjcBinding.Tasks":
|
|
case "Xamarin.MacDev":
|
|
case "Xamarin.MacDev.Tasks":
|
|
// other stuff that is not part of the SDK but shipped in the same 2.1 directory
|
|
failed_bcl.Add (aname);
|
|
break;
|
|
default:
|
|
if (!ProfilePoker.IsWellKnownSdk (aname))
|
|
failed_bcl.Add (aname);
|
|
break;
|
|
}
|
|
}
|
|
CollectionAssert.IsEmpty (failed_bcl, "BCL");
|
|
}
|
|
|
|
void REPL (string path)
|
|
{
|
|
var repl = Path.Combine (path, "repl");
|
|
var failed_repl = new List<string> ();
|
|
foreach (var file in Directory.GetFiles (repl, "*.dll")) {
|
|
var aname = Path.GetFileNameWithoutExtension (file);
|
|
switch (aname) {
|
|
// sub-list that are SDK assemblies
|
|
case "mscorlib":
|
|
case "System":
|
|
case "System.Core":
|
|
case "System.Xml":
|
|
case "Mono.CSharp":
|
|
if (!ProfilePoker.IsWellKnownSdk (aname))
|
|
failed_repl.Add (aname);
|
|
break;
|
|
default:
|
|
failed_repl.Add (aname);
|
|
break;
|
|
}
|
|
}
|
|
CollectionAssert.IsEmpty (failed_repl, "Repl");
|
|
}
|
|
|
|
void Facades (string path)
|
|
{
|
|
var facades = Path.Combine (path, "Facades");
|
|
var failed_facades = new List<string> ();
|
|
foreach (var file in Directory.GetFiles (facades, "*.dll")) {
|
|
var aname = Path.GetFileNameWithoutExtension (file);
|
|
if (!ProfilePoker.IsWellKnownSdk (aname))
|
|
failed_facades.Add (aname);
|
|
}
|
|
CollectionAssert.IsEmpty (failed_facades, "Facades");
|
|
}
|
|
|
|
[Test]
|
|
public void iOS_Unified ()
|
|
{
|
|
BCL (UnifiedPath);
|
|
REPL (UnifiedPath);
|
|
Facades (UnifiedPath);
|
|
}
|
|
|
|
[Test]
|
|
public void tvOS ()
|
|
{
|
|
BCL (tvOSPath);
|
|
REPL (tvOSPath);
|
|
Facades (tvOSPath);
|
|
}
|
|
|
|
[Test]
|
|
public void watchOS ()
|
|
{
|
|
BCL (watchOSPath);
|
|
REPL (watchOSPath);
|
|
Facades (watchOSPath);
|
|
}
|
|
|
|
[Test]
|
|
public void NoAssemblyReferenceInAttributes ()
|
|
{
|
|
var dir = Path.Combine (Configuration.MonoTouchRootDirectory, "lib/mono");
|
|
var failed = new List<string> ();
|
|
var curdir = Environment.CurrentDirectory;
|
|
foreach (var filename in Directory.GetFiles (dir, "*.dll", SearchOption.AllDirectories)) {
|
|
// This tests verifies that there aren't any attributes in any assembly we ship
|
|
// that references an assembly that's not in the normal assembly references.
|
|
// It takes a significant amount of time to look in all the attributes for assembly references,
|
|
// and knowing that no such attributes exist in any assembly we ship, allows us
|
|
// to complete skip this step in mtouch
|
|
try {
|
|
Environment.CurrentDirectory = Path.GetDirectoryName (filename);
|
|
VerifyNoAdditionalAssemblyReferenceInAttributes (filename);
|
|
} catch (Exception e) {
|
|
Console.WriteLine ($"Failed to process {filename}: {e.ToString ()}");
|
|
failed.Add ($"Failed to process {filename}: {e.Message}");
|
|
} finally {
|
|
Environment.CurrentDirectory = curdir;
|
|
}
|
|
}
|
|
Assert.IsEmpty (string.Join ("\n", failed), "Failed files");
|
|
}
|
|
|
|
void VerifyNoAdditionalAssemblyReferenceInAttributes (string filename)
|
|
{
|
|
var references = new HashSet<AssemblyNameReference> ();
|
|
using (var assembly = AssemblyDefinition.ReadAssembly (filename)) {
|
|
var main = assembly.MainModule;
|
|
references.UnionWith (main.AssemblyReferences);
|
|
|
|
var pre_attributes = new HashSet<AssemblyNameReference> (references);
|
|
|
|
GetCustomAttributeReferences (assembly, references);
|
|
GetCustomAttributeReferences (main, references);
|
|
if (main.HasTypes) {
|
|
foreach (var ca in main.GetCustomAttributes ())
|
|
GetCustomAttributeReferences (ca, references);
|
|
}
|
|
|
|
var post_attributes = pre_attributes.Except (references).ToArray ();
|
|
Assert.IsEmpty (post_attributes, assembly.Name.Name);
|
|
}
|
|
}
|
|
|
|
void GetCustomAttributeReferences (ICustomAttributeProvider cap, HashSet<AssemblyNameReference> references)
|
|
{
|
|
if (!cap.HasCustomAttributes)
|
|
return;
|
|
foreach (var ca in cap.CustomAttributes)
|
|
GetCustomAttributeReferences (ca, references);
|
|
}
|
|
|
|
static void GetCustomAttributeReferences (CustomAttribute ca, HashSet<AssemblyNameReference> references)
|
|
{
|
|
if (ca.HasConstructorArguments) {
|
|
foreach (var arg in ca.ConstructorArguments)
|
|
GetCustomAttributeArgumentReference (arg, references);
|
|
}
|
|
if (ca.HasFields) {
|
|
foreach (var arg in ca.Fields)
|
|
GetCustomAttributeArgumentReference (arg.Argument, references);
|
|
}
|
|
if (ca.HasProperties) {
|
|
foreach (var arg in ca.Properties)
|
|
GetCustomAttributeArgumentReference (arg.Argument, references);
|
|
}
|
|
}
|
|
|
|
static void GetCustomAttributeArgumentReference (CustomAttributeArgument arg, HashSet<AssemblyNameReference> references)
|
|
{
|
|
if (!arg.Type.Is ("System", "Type"))
|
|
return;
|
|
var ar = (arg.Value as TypeReference)?.Scope as AssemblyNameReference;
|
|
if (ar is null)
|
|
return;
|
|
references.Add (ar);
|
|
}
|
|
|
|
static string [] GetWatchOSAssemblies ()
|
|
{
|
|
var rv = new List<string> ();
|
|
|
|
rv.AddRange (Directory.GetFiles (watchOSPath, "*.dll", SearchOption.TopDirectoryOnly).
|
|
Select ((v) => v.Substring (watchOSPath.Length)).ToArray ());
|
|
rv.Remove ("Xamarin.WatchOS.dll");
|
|
rv.Add (Path.Combine ("..", "..", "32bits", "watchOS", "Xamarin.WatchOS.dll"));
|
|
return rv.ToArray ();
|
|
}
|
|
|
|
static Dictionary<string, Tuple<int /* expected exit code */, string [] /* expected 'LLVM failed' lines */>> known_llvm_failures = new Dictionary<string, Tuple<int, string []>> {
|
|
{ "System.Data.dll", new Tuple<int, string[]> (0, new string [] {
|
|
"LLVM failed for 'XmlDataDocument.HasPointers': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'XmlDataDocument.OnFoliated': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'XmlDataDocument.SetRowValueFromXmlText': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'Constraint.CheckStateForProperty': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'ConstraintCollection.Clear': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataColumn.set_Expression': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataColumnCollection.BaseAdd': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataColumnCollection.Clear': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataRelation.CheckStateForProperty': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataRow.set_Item': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataRow.set_ItemArray': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataSet.SetLocaleValue': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataTable.RestoreIndexEvents': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataTable.set_Locale': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataTable.NewRecordFromArray': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataTable.RaiseRowChanged': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataTable.SetNewRecordWorker': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataView.OnListChanged': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataView.SetDataViewManager': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataViewManager.OnListChanged': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataExpression.Evaluate': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataExpression.ToBoolean': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'ExpressionParser.Parse': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'ExpressionParser.ParseAggregateArgument': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'Merger.MergeRelation': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'RecordManager.CopyRecord': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'Select.AcceptRecord': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'XDRSchema.GetMinMax': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'XmlTreeGen.SetMSDataAttribute': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'SNITCPHandle.ReceiveAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'SchemaMapping.SetupSchemaWithoutKeyInfo': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'SchemaMapping.SetupSchemaWithKeyInfo': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DbCommandBuilder.RowUpdatingHandler': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataAdapter.FillLoadDataRowChunk': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataAdapter.FillLoadDataRow': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataAdapter.FillMapping': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataAdapter.FillNextResult': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataRecordInternal.GetBytes': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataRecordInternal.GetChars': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DbDataAdapter.Update': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'DataSetRelationCollection.AddCore': non-finally/catch/fault clause.",
|
|
}) },
|
|
{ "System.dll", new Tuple<int, string[]> (0, new string [] {
|
|
"LLVM failed for 'WebClient.DownloadDataInternal': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.DownloadFile': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.OpenRead': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.OpenWrite': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.UploadDataInternal': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.UploadValues': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.AbortRequest': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.DownloadBits': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.UploadBits': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.GetStringUsingEncoding': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.OpenReadAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.OpenWriteAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.DownloadStringAsyncCallback': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.DownloadStringAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.DownloadDataAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.DownloadFileAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.UploadStringAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.UploadDataAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.UploadFileAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.UploadValuesAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'WebClient.<UploadStringAsync>b__179_0': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<DownloadBitsAsync>d__150.MoveNext': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<UploadBitsAsync>d__152.MoveNext': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<>c__DisplayClass164_0.<OpenReadAsync>b__0': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<>c__DisplayClass167_0.<OpenWriteAsync>b__0': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<WaitForWriteTaskAsync>d__55.MoveNext': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<SendFrameFallbackAsync>d__56.MoveNext': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<ReceiveAsyncPrivate>d__61`2.MoveNext': non-finally/catch/fault clause.",
|
|
"LLVM failed for '<ReceiveAsyncPrivate>d__61`2.MoveNext': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.Read': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.Write': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.BeginRead': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.EndRead': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.BeginWrite': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.EndWrite': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.ReadAsync': non-finally/catch/fault clause.",
|
|
"LLVM failed for 'NetworkStream.WriteAsync': non-finally/catch/fault clause.",
|
|
}) },
|
|
{ "System.Core.dll", new Tuple<int, string[]> (0, new string [] {
|
|
"LLVM failed for 'EnterTryCatchFinallyInstruction.Run': non-finally/catch/fault clause.",
|
|
}) },
|
|
{ "System.Net.Http.dll", new Tuple<int, string[]> (0, new string [] {
|
|
}) },
|
|
{ "mscorlib.dll", new Tuple<int, string[]> (0, new string [] {
|
|
"LLVM failed for 'Console.Write': opcode arglist",
|
|
"LLVM failed for 'Console.WriteLine': opcode arglist",
|
|
}) },
|
|
};
|
|
|
|
[Test]
|
|
[TestCaseSource ("GetWatchOSAssemblies")]
|
|
public void NoLLVMFailuresInWatchOS (string asm)
|
|
{
|
|
MTouch.AssertDeviceAvailable ();
|
|
|
|
// Run LLVM on every assembly we ship in watchOS, using the arguments we usually use when done from mtouch.
|
|
var aot_compiler = Path.Combine (Configuration.BinDirXI, "armv7k-unknown-darwin-mono-sgen");
|
|
var tmpdir = Cache.CreateTemporaryDirectory ();
|
|
var llvm_path = Path.Combine (Configuration.SdkRootXI, "LLVM", "bin");
|
|
var env = new Dictionary<string, string> {
|
|
{ "MONO_PATH", watchOSPath }
|
|
};
|
|
var arch = "armv7k";
|
|
var arch_dir = Path.Combine (tmpdir, arch);
|
|
Directory.CreateDirectory (arch_dir);
|
|
var args = new List<string> ();
|
|
args.Add ("--debug");
|
|
args.Add ("--llvm");
|
|
args.Add ("-O=float32");
|
|
args.Add ($"--aot=mtriple={arch}-ios" +
|
|
$",data-outfile={Path.Combine (arch_dir, Path.GetFileNameWithoutExtension (asm) + ".aotdata." + arch)}" +
|
|
$",static,asmonly,direct-icalls,llvmonly,nodebug,dwarfdebug,direct-pinvoke" +
|
|
$",msym-dir={Path.Combine (arch_dir, Path.GetFileNameWithoutExtension (asm) + ".mSYM")}" +
|
|
$",llvm-path={llvm_path}" +
|
|
$",llvm-outfile={Path.Combine (arch_dir, Path.GetFileName (asm) + ".bc")}");
|
|
args.Add (Path.Combine (watchOSPath, asm));
|
|
|
|
StringBuilder output = new StringBuilder ();
|
|
var rv = ExecutionHelper.Execute (aot_compiler, args, stdout: output, stderr: output, environmentVariables: env, timeout: TimeSpan.FromMinutes (5));
|
|
var llvm_failed = output.ToString ().Split ('\n').Where ((v) => v.Contains ("LLVM failed"));
|
|
Console.WriteLine (output);
|
|
|
|
int expected_exit_code = 0;
|
|
if (known_llvm_failures.TryGetValue (asm, out var known_failures)) {
|
|
expected_exit_code = known_failures.Item1;
|
|
Assert.AreEqual (expected_exit_code, rv, "AOT compilation");
|
|
if (known_failures.Item2 is not null) {
|
|
// Check if there are known failures for failures we've fixed
|
|
var known_inexistent_failures = known_failures.Item2.Where ((v) => !llvm_failed.Contains (v));
|
|
Assert.IsEmpty (string.Join ("\n", known_inexistent_failures), $"Redundant known failures: should be removed from dictionary for {asm}");
|
|
// Filter the known failures from the failed llvm lines.
|
|
llvm_failed = llvm_failed.Where ((v) => !known_failures.Item2.Contains (v));
|
|
}
|
|
}
|
|
|
|
Assert.AreEqual (expected_exit_code, rv, "AOT compilation");
|
|
Assert.IsEmpty (string.Join ("\n", llvm_failed), "LLVM failed");
|
|
}
|
|
}
|
|
}
|