xamarin-macios/tests/mtouch/MTouchTool.cs

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

2016-05-26 16:06:52 +03:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Xml;
using Xamarin.Tests;
using Xamarin.Utils;
2016-05-26 16:06:52 +03:00
using NUnit.Framework;
namespace Xamarin
2016-05-26 16:06:52 +03:00
{
public enum MTouchAction
{
None,
BuildDev,
BuildSim,
LaunchSim,
2016-05-26 16:06:52 +03:00
}
[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
public enum MTouchSymbolMode
{
Unspecified,
Default,
Linker,
Code,
Ignore,
}
public enum MTouchBitcode
{
Unspecified,
ASMOnly,
Full, // LLVMOnly
Marker,
}
class MTouchTool : BundlerTool, IDisposable
2016-05-26 16:06:52 +03:00
{
#pragma warning disable 649
// These map directly to mtouch options
Optimize calls to BlockLiteral.SetupBlock to inject the block signature. (#3391) * [linker] Optimize calls to BlockLiteral.SetupBlock to inject the block signature. Optimize calls to BlockLiteral.SetupBlock[Unsafe] to calculate the block signature at build time, and inject it into the call site. This makes block invocations 10-15x faster (I've added tests that asserts at least an 8x increase). It's also required in order to be able to remove the dynamic registrar code in the future (since calculating the block signature at runtime requires the dynamic registrar). * [mtouch/mmp] Add support for reporting errors/warnings that point to the code line causing the error/warning. Add support for reporting errors/warnings that point to the code line causing the error/warning by adding ErrorHelper overloads that take the exact instruction to report (previously we defaulted to the first line/instruction in a method). * [tests] Add support for asserting filename/linenumber in warning messages. * Make all methods that manually create BlockLiterals optimizable. * [tests] Create a BaseOptimizeGeneratedCodeTest test that's included in both XI's and XM's link all test. * [tests] Add link all test (for both XI and XM) to test the BlockLiteral.SetupBlock optimization. * [tests] Add mtouch/mmp tests for the BlockLiteral.SetupBlock optimization. * [tests][linker] Make the base test class abstract, so tests in the base class aren't executed twice. * [tests][linker] Don't execute linkall-only tests in linksdk. The optimization tests only apply when the test assembly is linked, and that only happens in linkall, so exclude those tests in linksdk. * [tests][mmptest] Update test according to mmp changes. Fixes these test failures: 1) Failed : Xamarin.MMP.Tests.MMPTests.MM0132("inline-runtime-arch") The warning 'MM0132: Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' was not found in the output: Message #1 did not match: actual: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' Message #2 did not match: actual: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' 2) Failed : Xamarin.MMP.Tests.MMPTests.MM0132("foo") The warning 'MM0132: Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' was not found in the output: Message #1 did not match: actual: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' Message #2 did not match: actual: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' * [tests][linker] Fix typo. Fixes this test failure: 1) SetupBlock_CustomDelegate (Linker.Shared.BaseOptimizeGeneratedCodeTest.SetupBlock_CustomDelegate) Counter Expected: 1 But was: 2 * [registrar] Minor adjustment to error message to match previous (and better) behavior. Fixes this test failure: 1) Failed : Xamarin.Registrar.GenericType_WithInvalidParameterTypes The error 'MT4136: The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1<U>' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1<U>)'' was not found in the output: Message #1 did not match: actual: 'The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1<Foundation.NSObject>' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1<U>)'' expected: 'The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1<U>' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1<U>)'' * [docs] mmp shows MM errors/warnings. * [docs] Improve according to reviews. * [tests] Fix merge failure causing test duplication.
2018-02-06 09:08:15 +03:00
public MTouchAction? Action; // --sim, --dev, --launchsim, etc
2016-05-26 16:06:52 +03:00
public bool? NoSign;
public bool? FastDev;
public bool? Dlsym;
public string Executable;
public string AppPath;
public string Device; // --device
[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
public MTouchSymbolMode SymbolMode;
2016-05-26 16:06:52 +03:00
public bool? NoFastSim;
public List<MTouchTool> AppExtensions = new List<MTouchTool> ();
public List<string> Frameworks = new List<string> ();
public bool? PackageMdb;
public bool? MSym;
public bool? DSym;
public bool? NoStrip;
public string NoSymbolStrip;
public string Mono;
2016-05-26 16:06:52 +03:00
// These are a bit smarter
public List<string> AssemblyBuildTargets = new List<string> ();
2016-05-26 16:06:52 +03:00
static XmlDocument device_list_cache;
public string LLVMOptimizations;
public MTouchBitcode Bitcode;
public string AotArguments;
public string AotOtherArguments;
public string SymbolList;
#pragma warning restore 649
2016-05-26 16:06:52 +03:00
public MTouchTool ()
{
Profile = Profile.iOS;
}
2016-05-26 16:06:52 +03:00
public class DeviceInfo
{
public string UDID;
public string Name;
public string CompanionIdentifier;
public string DeviceClass;
public DeviceInfo Companion;
}
public int LaunchOnDevice (DeviceInfo device, string appPath, bool waitForUnlock, bool waitForExit)
{
return Execute ("--devname \"{0}\" --launchdev \"{1}\" --sdkroot \"{2}\" --wait-for-unlock:{3} --wait-for-exit:{4} {5}", device.Name, appPath, Configuration.xcode_root, waitForUnlock ? "yes" : "no", waitForExit ? "yes" : "no", GetVerbosity ());
}
public int InstallOnDevice (DeviceInfo device, string appPath, string devicetype = null)
{
return Execute ("--devname \"{0}\" --installdev \"{1}\" --sdkroot \"{2}\" {3} {4}", device.Name, appPath, Configuration.xcode_root, GetVerbosity (), devicetype == null ? string.Empty : "--device " + devicetype);
}
// The default is to build for the simulator
public override int Execute ()
{
if (!Action.HasValue)
Action = MTouchAction.BuildSim;
return base.Execute ();
}
2016-05-26 16:06:52 +03:00
public int Execute (MTouchAction action)
{
this.Action = action;
return base.Execute ();
}
// The default is to build for the simulator
public override void AssertExecute (string message = null)
{
if (!Action.HasValue)
Action = MTouchAction.BuildSim;
base.AssertExecute (message);
2016-05-26 16:06:52 +03:00
}
public void AssertExecute (MTouchAction action, string message = null)
{
this.Action = action;
base.AssertExecute (message);
}
public void AssertExecuteFailure (MTouchAction action, string message = null)
{
Optimize calls to BlockLiteral.SetupBlock to inject the block signature. (#3391) * [linker] Optimize calls to BlockLiteral.SetupBlock to inject the block signature. Optimize calls to BlockLiteral.SetupBlock[Unsafe] to calculate the block signature at build time, and inject it into the call site. This makes block invocations 10-15x faster (I've added tests that asserts at least an 8x increase). It's also required in order to be able to remove the dynamic registrar code in the future (since calculating the block signature at runtime requires the dynamic registrar). * [mtouch/mmp] Add support for reporting errors/warnings that point to the code line causing the error/warning. Add support for reporting errors/warnings that point to the code line causing the error/warning by adding ErrorHelper overloads that take the exact instruction to report (previously we defaulted to the first line/instruction in a method). * [tests] Add support for asserting filename/linenumber in warning messages. * Make all methods that manually create BlockLiterals optimizable. * [tests] Create a BaseOptimizeGeneratedCodeTest test that's included in both XI's and XM's link all test. * [tests] Add link all test (for both XI and XM) to test the BlockLiteral.SetupBlock optimization. * [tests] Add mtouch/mmp tests for the BlockLiteral.SetupBlock optimization. * [tests][linker] Make the base test class abstract, so tests in the base class aren't executed twice. * [tests][linker] Don't execute linkall-only tests in linksdk. The optimization tests only apply when the test assembly is linked, and that only happens in linkall, so exclude those tests in linksdk. * [tests][mmptest] Update test according to mmp changes. Fixes these test failures: 1) Failed : Xamarin.MMP.Tests.MMPTests.MM0132("inline-runtime-arch") The warning 'MM0132: Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' was not found in the output: Message #1 did not match: actual: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' Message #2 did not match: actual: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'inline-runtime-arch'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' 2) Failed : Xamarin.MMP.Tests.MMPTests.MM0132("foo") The warning 'MM0132: Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' was not found in the output: Message #1 did not match: actual: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' Message #2 did not match: actual: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock.' expected: 'Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size.' * [tests][linker] Fix typo. Fixes this test failure: 1) SetupBlock_CustomDelegate (Linker.Shared.BaseOptimizeGeneratedCodeTest.SetupBlock_CustomDelegate) Counter Expected: 1 But was: 2 * [registrar] Minor adjustment to error message to match previous (and better) behavior. Fixes this test failure: 1) Failed : Xamarin.Registrar.GenericType_WithInvalidParameterTypes The error 'MT4136: The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1<U>' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1<U>)'' was not found in the output: Message #1 did not match: actual: 'The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1<Foundation.NSObject>' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1<U>)'' expected: 'The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1<U>' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1<U>)'' * [docs] mmp shows MM errors/warnings. * [docs] Improve according to reviews. * [tests] Fix merge failure causing test duplication.
2018-02-06 09:08:15 +03:00
Action = action;
NUnit.Framework.Assert.AreEqual (1, Execute (), message);
}
// Assert that none of the files in the app has changed (except 'except' files)
public void AssertNoneModified (DateTime timestamp, string message, params string [] except)
{
var failed = new List<string> ();
var files = Directory.EnumerateFiles (AppPath, "*", SearchOption.AllDirectories);
foreach (var file in files) {
var info = new FileInfo (file);
if (info.LastWriteTime > timestamp) {
if (except != null && except.Contains (Path.GetFileName (file))) {
Console.WriteLine ("SKIP: {0} modified: {1} > {2}", file, info.LastWriteTime, timestamp);
} else {
failed.Add (string.Format ("{0} is modified, timestamp: {1} > {2}", file, info.LastWriteTime, timestamp));
Console.WriteLine ("FAIL: {0} modified: {1} > {2}", file, info.LastWriteTime, timestamp);
}
} else {
Console.WriteLine ("{0} not modified ted: {1} <= {2}", file, info.LastWriteTime, timestamp);
}
}
Assert.IsEmpty (failed, message);
}
// Assert that all of the files in the app has changed (except 'except' files)
public void AssertAllModified (DateTime timestamp, string message, params string [] except)
{
var failed = new List<string> ();
var files = Directory.EnumerateFiles (AppPath, "*", SearchOption.AllDirectories);
foreach (var file in files) {
var info = new FileInfo (file);
if (info.LastWriteTime <= timestamp) {
if (except != null && except.Contains (Path.GetFileName (file))) {
Console.WriteLine ("SKIP: {0} not modified: {1} <= {2}", file, info.LastWriteTime, timestamp);
} else {
failed.Add (string.Format ("{0} is not modified, timestamp: {1} <= {2}", file, info.LastWriteTime, timestamp));
Console.WriteLine ("FAIL: {0} not modified: {1} <= {2}", file, info.LastWriteTime, timestamp);
}
} else {
Console.WriteLine ("{0} modified (as expected): {1} > {2}", file, info.LastWriteTime, timestamp);
}
}
Assert.IsEmpty (failed, message);
}
// Asserts that the given files were modified.
public void AssertModified (DateTime timestamp, string message, params string [] files)
{
Assert.IsNotEmpty (files);
var failed = new List<string> ();
var fs = Directory.EnumerateFiles (AppPath, "*", SearchOption.AllDirectories);
foreach (var file in fs) {
if (!files.Contains (Path.GetFileName (file)))
continue;
var info = new FileInfo (file);
if (info.LastWriteTime < timestamp) {
failed.Add (string.Format ("{0} is not modified, timestamp: {1} < {2}", file, info.LastWriteTime, timestamp));
Console.WriteLine ("FAIL: {0} not modified: {1} < {2}", file, info.LastWriteTime, timestamp);
} else {
Console.WriteLine ("{0} modified (as expected): {1} >= {2}", file, info.LastWriteTime, timestamp);
}
}
Assert.IsEmpty (failed, message);
}
protected override string GetDefaultAbi()
2016-05-26 16:06:52 +03:00
{
var isDevice = false;
switch (Action.Value) {
case MTouchAction.None:
break;
case MTouchAction.BuildDev:
isDevice = true;
break;
case MTouchAction.BuildSim:
isDevice = false;
break;
case MTouchAction.LaunchSim:
isDevice = false;
break;
default:
throw new NotImplementedException ();
}
switch (Profile) {
case Profile.iOS:
return null; // not required
case Profile.tvOS:
return isDevice ? "arm64" : "x86_64";
case Profile.watchOS:
return isDevice ? "armv7k" : "i386";
default:
throw new NotImplementedException ();
}
}
protected override void BuildArguments (StringBuilder sb)
{
base.BuildArguments (sb);
switch (Action.Value) {
2016-05-26 16:06:52 +03:00
case MTouchAction.None:
break;
case MTouchAction.BuildDev:
MTouch.AssertDeviceAvailable ();
2016-05-26 16:06:52 +03:00
if (AppPath == null)
throw new Exception ("No AppPath specified.");
sb.Append (" --dev ").Append (StringUtils.Quote (AppPath));
2016-05-26 16:06:52 +03:00
break;
case MTouchAction.BuildSim:
if (AppPath == null)
throw new Exception ("No AppPath specified.");
sb.Append (" --sim ").Append (StringUtils.Quote (AppPath));
2016-05-26 16:06:52 +03:00
break;
case MTouchAction.LaunchSim:
if (AppPath == null)
throw new Exception ("No AppPath specified.");
sb.Append (" --launchsim ").Append (StringUtils.Quote (AppPath));
break;
2016-05-26 16:06:52 +03:00
default:
throw new NotImplementedException ();
}
if (FastDev.HasValue && FastDev.Value)
sb.Append (" --fastdev");
if (PackageMdb.HasValue)
sb.Append (" --package-mdb:").Append (PackageMdb.Value ? "true" : "false");
if (NoStrip.HasValue && NoStrip.Value)
sb.Append (" --nostrip");
if (NoSymbolStrip != null) {
if (NoSymbolStrip.Length == 0) {
sb.Append (" --nosymbolstrip");
} else {
sb.Append (" --nosymbolstrip:").Append (NoSymbolStrip);
}
}
if (!string.IsNullOrEmpty (SymbolList))
sb.Append (" --symbollist=").Append (StringUtils.Quote (SymbolList));
if (MSym.HasValue)
sb.Append (" --msym:").Append (MSym.Value ? "true" : "false");
if (DSym.HasValue)
sb.Append (" --dsym:").Append (DSym.Value ? "true" : "false");
foreach (var appext in AppExtensions)
sb.Append (" --app-extension ").Append (StringUtils.Quote (appext.AppPath));
foreach (var framework in Frameworks)
sb.Append (" --framework ").Append (StringUtils.Quote (framework));
if (!string.IsNullOrEmpty (Mono))
sb.Append (" --mono:").Append (StringUtils.Quote (Mono));
2016-05-26 16:06:52 +03:00
if (Dlsym.HasValue)
sb.Append (" --dlsym:").Append (Dlsym.Value ? "true" : "false");
2016-05-26 16:06:52 +03:00
if (!string.IsNullOrEmpty (Executable))
sb.Append (" --executable ").Append (StringUtils.Quote (Executable));
[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
switch (SymbolMode) {
case MTouchSymbolMode.Ignore:
sb.Append (" --dynamic-symbol-mode=ignore");
break;
case MTouchSymbolMode.Code:
sb.Append (" --dynamic-symbol-mode=code");
break;
case MTouchSymbolMode.Default:
sb.Append (" --dynamic-symbol-mode=default");
break;
case MTouchSymbolMode.Linker:
sb.Append (" --dynamic-symbol-mode=linker");
break;
case MTouchSymbolMode.Unspecified:
break;
default:
throw new NotImplementedException ();
}
2016-05-26 16:06:52 +03:00
if (NoFastSim.HasValue && NoFastSim.Value)
sb.Append (" --nofastsim");
if (!string.IsNullOrEmpty (Device))
sb.Append (" --device:").Append (StringUtils.Quote (Device));
if (!string.IsNullOrEmpty (LLVMOptimizations))
sb.Append (" --llvm-opt=").Append (StringUtils.Quote (LLVMOptimizations));
if (Bitcode != MTouchBitcode.Unspecified)
sb.Append (" --bitcode:").Append (Bitcode.ToString ().ToLower ());
foreach (var abt in AssemblyBuildTargets)
sb.Append (" --assembly-build-target=").Append (StringUtils.Quote (abt));
if (!string.IsNullOrEmpty (AotArguments))
sb.Append (" --aot:").Append (StringUtils.Quote (AotArguments));
if (!string.IsNullOrEmpty (AotOtherArguments))
sb.Append (" --aot-options:").Append (StringUtils.Quote (AotOtherArguments));
2016-05-26 16:06:52 +03:00
}
XmlDocument FetchDeviceList (bool allowCache = true)
{
if (device_list_cache == null || !allowCache) {
var output_file = Path.GetTempFileName ();
try {
if (Execute ("--listdev:{1} --sdkroot {0} --output-format xml", Configuration.xcode_root, output_file) != 0)
throw new Exception ("Failed to list devices.");
device_list_cache = new XmlDocument ();
device_list_cache.Load (output_file);
} finally {
File.Delete (output_file);
}
}
return device_list_cache;
}
public List<DeviceInfo> ListDevices ()
{
var rv = new List<DeviceInfo> ();
foreach (XmlNode node in FetchDeviceList ().SelectNodes ("//MTouch/Device")) {
rv.Add (new DeviceInfo () {
UDID = node.SelectSingleNode ("UDID")?.InnerText,
Name = node.SelectSingleNode ("Name")?.InnerText,
CompanionIdentifier = node.SelectSingleNode ("CompanionIdentifier")?.InnerText,
DeviceClass = node.SelectSingleNode ("DeviceClass")?.InnerText,
});
}
foreach (var device in rv) {
if (string.IsNullOrEmpty (device.CompanionIdentifier))
continue;
device.Companion = rv.FirstOrDefault ((d) => d.UDID == device.CompanionIdentifier);
}
return rv;
}
public IEnumerable<DeviceInfo> FindAvailableDevices (string [] deviceClasses)
{
return ListDevices ().Where ((info) => deviceClasses.Contains (info.DeviceClass));
}
public string NativeExecutablePath {
get {
return Path.Combine (AppPath, Path.GetFileNameWithoutExtension (RootAssembly));
2016-05-26 16:06:52 +03:00
}
}
public static string CreatePlist (Profile profile, string appName)
{
string plist = null;
switch (profile) {
case Profile.iOS:
plist = string.Format (@"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0"">
<dict>
<key>CFBundleDisplayName</key>
<string>{0}</string>
<key>CFBundleIdentifier</key>
<string>com.xamarin.{0}</string>
<key>CFBundleExecutable</key>
<string>{0}</string>
<key>MinimumOSVersion</key>
<string>{1}</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
", appName, MTouch.GetSdkVersion (profile));
break;
case Profile.tvOS:
plist = string.Format (@"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0"">
<dict>
<key>CFBundleDisplayName</key>
<string>Extensiontest</string>
<key>CFBundleIdentifier</key>
<string>com.xamarin.{0}</string>
<key>CFBundleExecutable</key>
<string>{0}</string>
<key>MinimumOSVersion</key>
<string>{1}</string>
<key>UIDeviceFamily</key>
<array>
<integer>3</integer>
</array>
</dict>
</plist>
", appName, MTouch.GetSdkVersion (profile));
break;
default:
throw new Exception ("Profile not specified.");
}
return plist;
}
2017-02-01 17:51:31 +03:00
public string CreateTemporarySatelliteAssembly (string culture = "en-AU")
{
var asm_dir = Path.Combine (Path.GetDirectoryName (RootAssembly), culture);
Directory.CreateDirectory (asm_dir);
var asm_name = Path.GetFileNameWithoutExtension (RootAssembly) + ".resources.dll";
// Cheat a bit, by compiling a normal assembly with code instead of creating a resource assembly
return MTouch.CompileTestAppLibrary (asm_dir, "class X {}", appName: Path.GetFileNameWithoutExtension (asm_name));
}
public override void CreateTemporaryApp (Profile profile, string appName = "testApp", string code = null, string extraArg = "", string extraCode = null, string usings = null, bool use_csc = false)
{
Profile = profile;
CreateTemporaryApp (appName: appName, code: code, extraArg: extraArg, extraCode: extraCode, usings: usings, use_csc: use_csc);
}
public void CreateTemporaryApp (bool hasPlist = false, string appName = "testApp", string code = null, string extraArg = "", string extraCode = null, string usings = null, bool use_csc = false)
2016-05-26 16:06:52 +03:00
{
string testDir;
if (RootAssembly == null) {
testDir = CreateTemporaryDirectory ();
} else {
// We're rebuilding an existing executable, so just reuse that
testDir = Path.GetDirectoryName (RootAssembly);
}
var app = AppPath ?? Path.Combine (testDir, appName + ".app");
2016-05-26 16:06:52 +03:00
Directory.CreateDirectory (app);
AppPath = app;
RootAssembly = CompileTestAppExecutable (testDir, code, extraArg, Profile, appName, extraCode, usings, use_csc);
if (hasPlist)
File.WriteAllText (Path.Combine (app, "Info.plist"), CreatePlist (Profile, appName));
2016-05-26 16:06:52 +03:00
}
2017-02-02 16:31:31 +03:00
public void CreateTemporaryServiceExtension (string code = null, string extraCode = null, string extraArg = null, string appName = "testServiceExtension")
{
string testDir;
if (RootAssembly == null) {
testDir = CreateTemporaryDirectory ();
} else {
// We're rebuilding an existing executable, so just reuse that
testDir = Path.GetDirectoryName (RootAssembly);
}
var app = AppPath ?? Path.Combine (testDir, $"{appName}.appex");
Directory.CreateDirectory (app);
if (code == null) {
code = @"using UserNotifications;
[Foundation.Register (""NotificationService"")]
public partial class NotificationService : UNNotificationServiceExtension
{
protected NotificationService (System.IntPtr handle) : base (handle) {}
}";
}
if (extraCode != null)
code += extraCode;
AppPath = app;
Extension = true;
RootAssembly = MTouch.CompileTestAppLibrary (testDir, code: code, profile: Profile, extraArg: extraArg, appName: appName);
var info_plist =
@"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0"">
<dict>
<key>CFBundleDisplayName</key>
<string>serviceextension</string>
<key>CFBundleName</key>
<string>serviceextension</string>
<key>CFBundleIdentifier</key>
<string>com.xamarin.testapp.serviceextension</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>10.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>NotificationService</string>
</dict>
</dict>
</plist>
";
var plist_path = Path.Combine (app, "Info.plist");
if (!File.Exists (plist_path) || File.ReadAllText (plist_path) != info_plist)
File.WriteAllText (plist_path, info_plist);
}
public void CreateTemporaryTodayExtension (string code = null, string extraCode = null, string extraArg = null, string appName = "testTodayExtension")
{
string testDir;
if (RootAssembly == null) {
testDir = CreateTemporaryDirectory ();
} else {
// We're rebuilding an existing executable, so just reuse that
testDir = Path.GetDirectoryName (RootAssembly);
}
var app = AppPath ?? Path.Combine (testDir, $"{appName}.appex");
Directory.CreateDirectory (app);
if (code == null) {
code = @"using System;
using Foundation;
using NotificationCenter;
using UIKit;
public partial class TodayViewController : UIViewController, INCWidgetProviding
{
public TodayViewController (IntPtr handle) : base (handle)
{
}
[Export (""widgetPerformUpdateWithCompletionHandler:"")]
public void WidgetPerformUpdate (Action<NCUpdateResult> completionHandler)
{
completionHandler (NCUpdateResult.NewData);
}
}
";
}
if (extraCode != null)
code += extraCode;
AppPath = app;
Extension = true;
RootAssembly = MTouch.CompileTestAppLibrary (testDir, code: code, profile: Profile, extraArg: extraArg, appName: appName);
var info_plist = // FIXME: this includes a NSExtensionMainStoryboard key which points to a non-existent storyboard. This won't matter as long as we're only building, and not running the extension.
@"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0"">
<dict>
<key>CFBundleDisplayName</key>
<string>todayextension</string>
<key>CFBundleName</key>
<string>todayextension</string>
<key>CFBundleIdentifier</key>
<string>com.xamarin.testapp.todayextension</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>10.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>widget-extension</string>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
</dict>
</dict>
</plist>
";
var plist_path = Path.Combine (app, "Info.plist");
if (!File.Exists (plist_path) || File.ReadAllText (plist_path) != info_plist)
File.WriteAllText (plist_path, info_plist);
}
public void CreateTemporaryWatchKitExtension (string code = null)
{
string testDir;
if (RootAssembly == null) {
testDir = CreateTemporaryDirectory ();
} else {
// We're rebuilding an existing executable, so just reuse that directory
testDir = Path.GetDirectoryName (RootAssembly);
}
var app = AppPath ?? Path.Combine (testDir, "testApp.appex");
Directory.CreateDirectory (app);
if (code == null) {
code = @"using WatchKit;
public partial class NotificationController : WKUserNotificationInterfaceController
{
protected NotificationController (System.IntPtr handle) : base (handle) {}
}";
}
AppPath = app;
Extension = true;
RootAssembly = MTouch.CompileTestAppLibrary (testDir, code: code, profile: Profile);
File.WriteAllText (Path.Combine (app, "Info.plist"), @"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0"">
<dict>
<key>CFBundleDisplayName</key>
<string>testapp</string>
<key>CFBundleName</key>
<string>testapp</string>
<key>CFBundleIdentifier</key>
<string>com.xamarin.testapp</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>2.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>com.xamarin.testapp.watchkitapp</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
<key>RemoteInterfacePrincipleClass</key>
<string>InterfaceController</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
</dict>
</plist>
");
}
2016-05-26 16:06:52 +03:00
public void CreateTemporaryApp_LinkWith ()
{
AppPath = CreateTemporaryAppDirectory ();
RootAssembly = MTouch.CompileTestAppExecutableLinkWith (Path.GetDirectoryName (AppPath), profile: Profile);
2016-05-26 16:06:52 +03:00
}
public string CreateTemporaryAppDirectory ()
{
if (AppPath != null)
throw new Exception ("There already is an App directory");
2016-05-26 16:06:52 +03:00
AppPath = Path.Combine (CreateTemporaryDirectory (), "testApp.app");
Directory.CreateDirectory (AppPath);
return AppPath;
}
void IDisposable.Dispose ()
{
}
public IEnumerable<string> NativeSymbolsInExecutable {
get {
return ExecutionHelper.Execute ("nm", $"-gUj {StringUtils.Quote (NativeExecutablePath)}", hide_output: true).Split ('\n');
}
}
protected override string ToolPath {
get { return Configuration.MtouchPath; }
}
protected override string MessagePrefix {
get { return "MT"; }
}
public override string GetAppAssembliesDirectory ()
{
return AppPath;
}
2016-05-26 16:06:52 +03:00
}
}