xamarin-macios/tools/common/Application.cs

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

2016-04-21 15:57:02 +03:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
2016-04-21 15:57:02 +03:00
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Linker;
using Mono.Tuner;
2016-04-21 15:57:02 +03:00
using Xamarin;
using Xamarin.Linker;
using Xamarin.MacDev;
2016-04-21 15:57:02 +03:00
using Xamarin.Utils;
using ObjCRuntime;
#if MONOTOUCH
using PlatformResolver = MonoTouch.Tuner.MonoTouchResolver;
#elif MMP
using PlatformResolver = Xamarin.Bundler.MonoMacResolver;
#elif NET
using PlatformResolver = Xamarin.Linker.DotNetResolver;
#else
#error Invalid defines
#endif
2016-04-21 15:57:02 +03:00
namespace Xamarin.Bundler {
public enum BuildTarget {
None,
Simulator,
Device,
}
2019-02-11 14:50:26 +03:00
public enum MonoNativeMode {
None,
Compat,
Unified,
}
2016-04-21 15:57:02 +03:00
[Flags]
public enum RegistrarOptions {
Default = 0,
Trace = 1,
}
public enum RegistrarMode {
Default,
Dynamic,
PartialStatic,
Static,
}
2016-04-21 15:57:02 +03:00
public partial class Application
{
public Cache Cache;
public string AppDirectory = ".";
2016-04-21 15:57:02 +03:00
public bool DeadStrip = true;
public bool EnableDebug;
// The list of assemblies that we do generate debugging info for.
public bool DebugAll;
public bool UseInterpreter; // Only applicable to mobile platforms.
public List<string> DebugAssemblies = new List<string> ();
2016-04-21 15:57:02 +03:00
internal RuntimeOptions RuntimeOptions;
[mtouch/mmp] Give users more control over optimizations, and share more code between mtouch and mmp. (#3242) * [mtouch/mmp] Give users more control over optimizations, and share more code between mtouch and mmp. 1. Add an --optimize flag to mtouch/mmp that allows users to select which optimizations to apply (or not). This makes it easier to add future optimizations, and allow users to disable any optimization that causes problems without having to disable many other features. 2. Share as much optimization code as possible between mtouch and mmp. This immediately gives a benefit to mmp, which has three new optimizations only mtouch had: NSObject.IsDirectBinding inlining, IntPtr.Size inlining and dead code elimination. This results in ~6kb of disk space saved for a linked Xamarin.Mac app: * link sdk: [Debug][1], [Release][2] * link all: [Debug][3], [Release][4] Testing also verifies that monotouchtest ([Debug][5], [Release][6]) has not changed size at all, which means that no default optimizations have changed inadvertedly. [1]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-sdk-mac--debug [2]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-sdk-mac--release [3]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-all-mac--debug [4]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-all-mac--release [5]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#monotouchtest-iphonedebug64 [6]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#monotouchtest-iphonerelease64 * [tools] Don't enable the IsDirectBinding optimization by default for Xamarin.Mac apps, it's not safe. * Fix whitespace issues. * [doc] Document optimizations. * Officially support optimizations by adding them to the Versions.plist. * [linker] Improve IntPtr.Size inliner + dead code eliminatior and add tests. * Properly handle operands for the ldc_i4_s instruction (they're sbyte). * Fix less-than condition to actually do a less-than comparison. * Make sure to look up the bitness in the Target, not the Application, since the Application's value will be incorrect when building fat apps (both Is32Build and Is64Build will be true). * Remove unnecessary checks for the IntPtr.Size inliner: this optimization does not depend on other instructions than the IntPtr.get_Size call, so remove the checks that verify surrounding instructions. This makes the IntPtr.Size inliner kick in in more scenarios (such as the new tests). * Add tests. * [tests] Add mmp tests for optimizations. * [tests] Fix XM optimization tests. * [tests] Fix test build error.
2018-01-23 13:33:48 +03:00
public Optimizations Optimizations = new Optimizations ();
2016-04-21 15:57:02 +03:00
public RegistrarMode Registrar = RegistrarMode.Default;
public RegistrarOptions RegistrarOptions = RegistrarOptions.Default;
[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 SymbolMode SymbolMode;
public HashSet<string> IgnoredSymbols = new HashSet<string> ();
2016-04-21 15:57:02 +03:00
// The AOT arguments are currently not used for macOS, but they could eventually be used there as well (there's no mmp option to set these yet).
public string AotArguments = "static,asmonly,direct-icalls,";
public List<string> AotOtherArguments = null;
public DlsymOptions DlsymOptions;
public List<Tuple<string, bool>> DlsymAssemblies;
public string CompilerPath;
public Application ContainerApp; // For extensions, this is the containing app
public bool IsCodeShared { get; private set; }
2016-04-21 15:57:02 +03:00
public HashSet<string> Frameworks = new HashSet<string> ();
public HashSet<string> WeakFrameworks = new HashSet<string> ();
public bool IsExtension;
2016-04-21 15:57:02 +03:00
public ApplePlatform Platform { get { return Driver.TargetFramework.Platform; } }
public List<string> InterpretedAssemblies = new List<string> ();
// EnableMSym: only implemented for Xamarin.iOS
bool? enable_msym;
public bool EnableMSym {
get { return enable_msym.Value; }
set { enable_msym = value; }
}
2016-04-21 15:57:02 +03:00
// Linker config
public LinkMode LinkMode = LinkMode.Full;
2016-04-21 15:57:02 +03:00
public List<string> LinkSkipped = new List<string> ();
public List<string> Definitions = new List<string> ();
public I18nAssemblies I18n;
public List<string> WarnOnTypeRef = new List<string> ();
2016-04-21 15:57:02 +03:00
public bool? EnableCoopGC;
public bool EnableSGenConc;
public bool EnableProfiling;
public bool? DebugTrack;
public Dictionary<string, string> EnvironmentVariables = new Dictionary<string, string> ();
public MarshalObjectiveCExceptionMode MarshalObjectiveCExceptions;
public MarshalManagedExceptionMode MarshalManagedExceptions;
bool is_default_marshal_managed_exception_mode;
public bool IsDefaultMarshalManagedExceptionMode {
get { return is_default_marshal_managed_exception_mode || MarshalManagedExceptions == MarshalManagedExceptionMode.Default; }
set { is_default_marshal_managed_exception_mode = value; }
}
public List<string> RootAssemblies = new List<string> ();
public List<string> References = new List<string> ();
[mtouch] Implement support for sharing code between app extensions and container apps. Implement support for sharing both code and resources between app extensions and their container app: * AOT-compiled code. Each shared assembly is only AOT-compiled once, and if the assembly is built to a framework or dynamic library, it will also only be included once in the final app (as a framework or dynamic library in the container app, referenced directly by the app extension). If the assemblies are built to static objects there won't be any size improvements in the app, but the build will be much faster, because the assemblies will only be AOT- compiled once. * Any resources related to managed assemblies (debug files, config files, satellite assemblies) will be put in the container app only. Since these improvements are significant, code sharing will be enabled by default. Test results ============ For an extreme test project with 7 extensions (embedded-frameworks)[1]: with code sharing cycle 9 difference build time 1m 47s 3m 33s -1m 46s = ~50% faster app size 26 MB 131 MB -105 MB = ~80% smaller For a more normal test project (MyTabbedApplication)[2] - this is a simple application with 1 extension: with code sharing cycle 9 difference build time 0m 44s 0m 48s -4s = ~ 8% faster app size 23 MB 37 MB -15 MB = ~40% smaller Another tvOS app with one extension also show similar gains (MyTVApp)[3]: with code sharing cycle 9 difference build time 0m 22s 0m 48s -26s = ~54% faster app size 22 MB 62 MB -40 MB = ~65% smaller [1]: https://github.com/rolfbjarne/embedded-frameworks [2]: https://github.com/xamarin/xamarin-macios/tree/cycle9/msbuild/tests/MyTabbedApplication [3]: https://github.com/xamarin/xamarin-macios/tree/cycle9/msbuild/tests/MyTVApp
2017-01-24 13:10:20 +03:00
public List<Application> SharedCodeApps = new List<Application> (); // List of appexes we're sharing code with.
public string RegistrarOutputLibrary;
public BuildTarget BuildTarget;
public bool? DisableLldbAttach = null; // Only applicable to Xamarin.Mac
public bool? DisableOmitFramePointer = null; // Only applicable to Xamarin.Mac
public string CustomBundleName = "MonoBundle"; // Only applicable to Xamarin.Mac and Mac Catalyst
public XamarinRuntime XamarinRuntime;
public bool? UseMonoFramework;
2020-10-23 17:52:33 +03:00
// The bitcode mode to compile to.
// This variable does not apply to macOS, because there's no bitcode on macOS.
public BitCodeMode BitCodeMode { get; set; }
public bool EnableAsmOnlyBitCode { get { return BitCodeMode == BitCodeMode.ASMOnly; } }
public bool EnableLLVMOnlyBitCode { get { return BitCodeMode == BitCodeMode.LLVMOnly; } }
public bool EnableMarkerOnlyBitCode { get { return BitCodeMode == BitCodeMode.MarkerOnly; } }
public bool EnableBitCode { get { return BitCodeMode != BitCodeMode.None; } }
2020-10-23 17:52:33 +03:00
// assembly_build_targets describes what kind of native code each assembly should be compiled into for mobile targets (iOS, tvOS, watchOS).
// An assembly can be compiled into: static object (.o), dynamic library (.dylib) or a framework (.framework).
// In the case of a framework, each framework may contain the native code for multiple assemblies.
// This variable does not apply to macOS (if assemblies are AOT-compiled, the AOT compiler will output a .dylib next to the assembly and there's nothing extra for us)
Dictionary<string, Tuple<AssemblyBuildTarget, string>> assembly_build_targets = new Dictionary<string, Tuple<AssemblyBuildTarget, string>> ();
public string ContentDirectory {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
return AppDirectory;
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
return Path.Combine (AppDirectory, "Contents", CustomBundleName);
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
public string FrameworksDirectory {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
return Path.Combine (AppDirectory, "Frameworks");
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
return Path.Combine (AppDirectory, "Contents", "Frameworks");
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
2020-10-23 17:52:33 +03:00
// How Mono should be embedded into the app.
public AssemblyBuildTarget LibMonoLinkMode {
get {
2020-10-23 17:52:33 +03:00
if (Platform == ApplePlatform.MacOSX) {
// This property was implemented for iOS, but might be re-used for macOS if desired after testing to verify it works as expected.
throw ErrorHelper.CreateError (99, Errors.MX0099, "LibMonoLinkMode isn't a valid operation for macOS apps.");
}
if (Embeddinator) {
return AssemblyBuildTarget.StaticObject;
} else if (HasFrameworks || UseMonoFramework.Value) {
return AssemblyBuildTarget.Framework;
} else if (HasDynamicLibraries) {
return AssemblyBuildTarget.DynamicLibrary;
} else {
return AssemblyBuildTarget.StaticObject;
}
}
}
2020-10-23 17:52:33 +03:00
// How libxamarin should be embedded into the app.
public AssemblyBuildTarget LibXamarinLinkMode {
get {
2020-10-23 17:52:33 +03:00
if (Platform == ApplePlatform.MacOSX) {
// This property was implemented for iOS, but might be re-used for macOS if desired after testing to verify it works as expected.
throw ErrorHelper.CreateError (99, Errors.MX0099, "LibXamarinLinkMode isn't a valid operation for macOS apps.");
}
if (Embeddinator) {
return AssemblyBuildTarget.StaticObject;
} else if (HasFrameworks) {
return AssemblyBuildTarget.Framework;
} else if (HasDynamicLibraries) {
return AssemblyBuildTarget.DynamicLibrary;
} else {
return AssemblyBuildTarget.StaticObject;
}
}
}
2020-10-23 17:52:33 +03:00
// How the generated libpinvoke library should be linked into the app.
public AssemblyBuildTarget LibPInvokesLinkMode => LibXamarinLinkMode;
2020-10-23 17:52:33 +03:00
// How the profiler library should be linked into the app.
public AssemblyBuildTarget LibProfilerLinkMode => OnlyStaticLibraries ? AssemblyBuildTarget.StaticObject : AssemblyBuildTarget.DynamicLibrary;
2020-10-23 17:52:33 +03:00
// How the libmononative library should be linked into the app.
public AssemblyBuildTarget LibMonoNativeLinkMode => HasDynamicLibraries ? AssemblyBuildTarget.DynamicLibrary : AssemblyBuildTarget.StaticObject;
2020-10-23 17:52:33 +03:00
// If all assemblies are compiled into static libraries.
public bool OnlyStaticLibraries {
get {
2020-10-23 17:52:33 +03:00
if (Platform == ApplePlatform.MacOSX)
throw ErrorHelper.CreateError (99, Errors.MX0099, "Using assembly_build_targets isn't a valid operation for macOS apps.");
return assembly_build_targets.All ((abt) => abt.Value.Item1 == AssemblyBuildTarget.StaticObject);
}
}
2020-10-23 17:52:33 +03:00
// If any assembly in the app is compiled into a dynamic library.
public bool HasDynamicLibraries {
get {
2020-10-23 17:52:33 +03:00
if (Platform == ApplePlatform.MacOSX)
throw ErrorHelper.CreateError (99, Errors.MX0099, "Using assembly_build_targets isn't a valid operation for macOS apps.");
return assembly_build_targets.Any ((abt) => abt.Value.Item1 == AssemblyBuildTarget.DynamicLibrary);
}
}
2020-10-23 17:52:33 +03:00
// If any assembly in the app is compiled into a framework.
public bool HasFrameworks {
get {
2020-10-23 17:52:33 +03:00
if (Platform == ApplePlatform.MacOSX)
throw ErrorHelper.CreateError (99, Errors.MX0099, "Using assembly_build_targets isn't a valid operation for macOS apps.");
return assembly_build_targets.Any ((abt) => abt.Value.Item1 == AssemblyBuildTarget.Framework);
}
}
2020-10-23 17:52:33 +03:00
// If this application has a Frameworks directory (or if any frameworks should be put in a containing app's Framework directory).
// This is used to know where to place embedded .frameworks (for app extensions they should go into the containing app's Frameworks directory).
// This logic works on all platforms.
public bool HasFrameworksDirectory {
get {
if (!IsExtension)
return true;
if (IsWatchExtension && Platform == ApplePlatform.WatchOS)
return true;
return false;
}
}
bool RequiresXcodeHeaders {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
case ApplePlatform.MacCatalyst:
return LinkMode == LinkMode.None;
case ApplePlatform.MacOSX:
return Registrar == RegistrarMode.Static && LinkMode == LinkMode.None;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
public string LocalBuildDir {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
case ApplePlatform.MacCatalyst:
return "_ios-build";
case ApplePlatform.MacOSX:
return "_mac-build";
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
public string FrameworkLocationVariable {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
case ApplePlatform.MacCatalyst:
return "MD_MTOUCH_SDK_ROOT";
case ApplePlatform.MacOSX:
return "XAMMAC_FRAMEWORK_PATH";
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
public bool IsDeviceBuild {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
return BuildTarget == BuildTarget.Device;
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
return false;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
public bool IsSimulatorBuild {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
return BuildTarget == BuildTarget.Simulator;
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
return false;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
public static int Concurrency => Driver.Concurrency;
public Version DeploymentTarget;
public Version SdkVersion; // for Mac Catalyst this is the iOS version
public Version NativeSdkVersion; // this is the same as SdkVersion, except that for Mac Catalyst it's the macOS SDK version.
public MonoNativeMode MonoNativeMode { get; set; }
2020-05-11 17:27:19 +03:00
List<Abi> abis;
public bool IsLLVM { get { return IsArchEnabled (Abi.LLVM); } }
2020-05-11 17:27:19 +03:00
public bool Embeddinator { get; set; }
public List<Target> Targets = new List<Target> ();
2019-02-11 14:50:26 +03:00
bool? package_managed_debug_symbols;
public bool PackageManagedDebugSymbols {
get { return package_managed_debug_symbols.Value; }
set { package_managed_debug_symbols = value; }
}
public string TlsProvider;
public string HttpMessageHandler;
// If we're targetting a 32 bit arch.
bool? is32bits;
public bool Is32Build {
get {
if (!is32bits.HasValue)
is32bits = IsArchEnabled (Abi.Arch32Mask);
return is32bits.Value;
}
}
public Version GetMacCatalystmacOSVersion (Version iOSVersion)
{
if (!MacCatalystSupport.TryGetMacOSVersion (Driver.GetFrameworkDirectory (this), iOSVersion, out var value))
throw ErrorHelper.CreateError (183, Errors.MX0183 /* Could not map the iOS version {0} to a macOS version for Mac Catalyst */, iOSVersion.ToString ());
return value;
}
public Version GetMacCatalystiOSVersion (Version macOSVersion)
{
if (!MacCatalystSupport.TryGetiOSVersion (Driver.GetFrameworkDirectory (this), macOSVersion, out var value))
throw ErrorHelper.CreateError (184, Errors.MX0184 /* Could not map the macOS version {0} to a corresponding iOS version for Mac Catalyst */, macOSVersion.ToString ());
return value;
}
public string GetProductName ()
{
return ProductName;
}
// If we're targetting a 64 bit arch.
bool? is64bits;
public bool Is64Build {
get {
if (!is64bits.HasValue)
is64bits = IsArchEnabled (Abi.Arch64Mask);
return is64bits.Value;
}
}
public bool IsDualBuild { get { return Is32Build && Is64Build; } } // if we're building both a 32 and a 64 bit version.
public Application ()
{
}
public Application (string [] arguments)
{
CreateCache (arguments);
}
public void CreateCache (string [] arguments)
{
Cache = new Cache (arguments);
}
public bool DynamicRegistrationSupported {
get {
return Optimizations.RemoveDynamicRegistrar != true;
}
}
public void ParseInterpreter (string value)
{
UseInterpreter = true;
if (!string.IsNullOrEmpty (value))
InterpretedAssemblies.AddRange (value.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries));
}
public void ParseI18nAssemblies (string i18n)
{
var assemblies = I18nAssemblies.None;
foreach (var part in i18n.Split (',')) {
var assembly = part.Trim ();
if (string.IsNullOrEmpty (assembly))
continue;
try {
assemblies |= (I18nAssemblies) Enum.Parse (typeof (I18nAssemblies), assembly, true);
} catch {
throw new FormatException ("Unknown value for i18n: " + assembly);
}
}
I18n = assemblies;
}
public bool IsTodayExtension {
get {
return ExtensionIdentifier == "com.apple.widget-extension";
}
}
public bool IsWatchExtension {
get {
return ExtensionIdentifier == "com.apple.watchkit";
}
}
public bool IsTVExtension {
get {
return ExtensionIdentifier == "com.apple.tv-services";
}
}
public string ExtensionIdentifier {
get {
if (!IsExtension)
return null;
var plist = Driver.FromPList (InfoPListPath);
var dict = plist.Get<PDictionary> ("NSExtension");
if (dict == null)
return null;
return dict.GetString ("NSExtensionPointIdentifier");
}
}
public string InfoPListPath {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
return Path.Combine (AppDirectory, "Info.plist");
case ApplePlatform.MacCatalyst:
case ApplePlatform.MacOSX:
return Path.Combine (AppDirectory, "Contents", "Info.plist");
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
[mtouch] Implement support for sharing code between app extensions and container apps. Implement support for sharing both code and resources between app extensions and their container app: * AOT-compiled code. Each shared assembly is only AOT-compiled once, and if the assembly is built to a framework or dynamic library, it will also only be included once in the final app (as a framework or dynamic library in the container app, referenced directly by the app extension). If the assemblies are built to static objects there won't be any size improvements in the app, but the build will be much faster, because the assemblies will only be AOT- compiled once. * Any resources related to managed assemblies (debug files, config files, satellite assemblies) will be put in the container app only. Since these improvements are significant, code sharing will be enabled by default. Test results ============ For an extreme test project with 7 extensions (embedded-frameworks)[1]: with code sharing cycle 9 difference build time 1m 47s 3m 33s -1m 46s = ~50% faster app size 26 MB 131 MB -105 MB = ~80% smaller For a more normal test project (MyTabbedApplication)[2] - this is a simple application with 1 extension: with code sharing cycle 9 difference build time 0m 44s 0m 48s -4s = ~ 8% faster app size 23 MB 37 MB -15 MB = ~40% smaller Another tvOS app with one extension also show similar gains (MyTVApp)[3]: with code sharing cycle 9 difference build time 0m 22s 0m 48s -26s = ~54% faster app size 22 MB 62 MB -40 MB = ~65% smaller [1]: https://github.com/rolfbjarne/embedded-frameworks [2]: https://github.com/xamarin/xamarin-macios/tree/cycle9/msbuild/tests/MyTabbedApplication [3]: https://github.com/xamarin/xamarin-macios/tree/cycle9/msbuild/tests/MyTVApp
2017-01-24 13:10:20 +03:00
// This is just a name for this app to show in log/error messages, etc.
public string Name {
get { return Path.GetFileNameWithoutExtension (AppDirectory); }
}
public bool RequiresPInvokeWrappers {
get {
if (Platform == ApplePlatform.MacOSX)
return false;
if (IsSimulatorBuild)
return false;
if (Platform == ApplePlatform.MacCatalyst)
return false;
return MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.ThrowManagedException || MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.Abort;
}
}
2016-04-21 15:57:02 +03:00
public string PlatformName {
get {
switch (Platform) {
case ApplePlatform.iOS:
return "iOS";
case ApplePlatform.TVOS:
return "tvOS";
case ApplePlatform.WatchOS:
return "watchOS";
case ApplePlatform.MacOSX:
return "macOS";
case ApplePlatform.MacCatalyst:
return "MacCatalyst";
2016-04-21 15:57:02 +03:00
default:
throw new NotImplementedException ();
}
}
}
[mtouch/mmp] Fix tracking of whether the static registrar should run again or not. Fixes #641. (#3534) * [tests] Improve debug spew for the RebuildTest_WithExtensions test. * [mtouch/mmp] Store/load if the dynamic registrar is removed or not into the cached link results. Store/load if the dynamic registrar is removed or not into the cached link results, so that we generate the correct main.m even if cached linker results are used. * [mtouch/mmp] The static registrar must not execute if we're loading cached results from the linker. The static registrar must not execute if we're loading cached results from the linker, because the static registrar needs information from the linker that's not restored from the cache. * [mtouch/mmp] Share Touch code. * [mtouch/mmp] Make it possible to touch inexistent files (to create them). * [mtouch/mmp] Fix tracking of whether the static registrar should run again or not. The recent changes to support optimizing away the dynamic registrar caused the Xamarin.MTouch.RebuildTest_WithExtensions test to regress. The problem ----------- * The linker now collects and stores information the static registrar needs. * This information is not restored from disk when the linker realizes that it can reload previously linked assemblies instead of executing again. * The static registrar runs again (for another reason). * The information the static registrar needs isn't available, and incorrect output follows. So fix 1: show an error if the static registrar runs when the linker loaded cached results. The exact scenario the test ran into is this: * 1st build: everything is new and everything is built. * 2nd build: contents of .exe changes, the linker runs again, the static registrar runs again, but sees that the generated output didn't change, so it doesn't write the new content to disk (this is an optimization to avoid compiling the registrar.m file again unless needed). * 3rd build: only the .exe timestamp changes, the linker sees nothing changes in the contents of the .exe and loads the previously linked assemblies from disk, the static registrar sees that the .exe's timestamp is newer than registrar.m's timestamp and run again, but doesn't produce the right result because it doesn't have the information it needs. Considered solutions -------------------- 1. Only track timestamps, not file contents. This is not ideal, since it will result in more work done: in particular for the case above, it would add a registrar.m compilation in build #2, and linker rerun + static registrar rerun + registrar.m compilation + final native link in build #3. 2. Always write the output of the static registrar, even if it hasn't changed. This is not ideal either, since it will also result in more work done: for the case above, it would add a registrar.m compilation + final native link in build #3. 3. Always write the output of the static registrar, but track if it changed or not, and if it didn't, just touch registrar.o instead of recompiling it. This only means the final native link in build #3 is added (see #5 for why this is worse than it sounds). 4. Always write the output of the static registrar, but track it it changed or not, and if it didn't, just touch registrar.o instead of recompiling it, and track that too, so that the final native link in build #3 isn't needed anymore. Unfortunately this may result in incorrect behavior, because now the msbuild tasks will detect that the executable has changed, and may run dsymutil + strip again. The executable didn't actually change, which means it would be the previously stripped executable, and thus we'd end up with an empty .dSYM because we ran dsymtil on an already stripped executable. 5. Idea #4, but write the output of the final link into a temporary directory instead of the .app, so that we could track whether we should update the executable in the .app or not. This is not optimal either, because executables can be *big* (I've seen multi-GB tvOS bitcode executables), and extra copies of such files should not be taken lightly. 6. Idea #4, but tell the MSBuild tasks that dsymutil/strip doesn't need to be rerun even if the timestamp of the executable changed. This might actually work, but now the solution's become quite complex. Implemented solution -------------------- Use stamp files to detect whether a file is up-to-date or not. In particular: * When we don't write to a file because the new contents are identical to the old contents, we now touch a .stamp file. This stamp file means "the accompanying file was determined to be up-to-date when the stamp was touched." * When checking whether a file is up-to-date, also check for the presence of a .stamp file, and if it exists, use the highest timestamp between the stamp file and the actual file. Now the test scenario becomes: * 1st build: everything is new and everything is built. * 2nd build: contents of .exe changes, the linker runs again, the static registrar runs again, but sees that the generated output didn't change, so it doesn't write the new content to disk, but it creates a registrar.m.stamp file to indicate the point in time when registrar.m was considered up-to- date. * 3rd build: only the .exe timestamp changes, the linker sees nothing changes in the contents of the .exe and loads the previously linked assemblies from disk, the static registrar sees that the .exe's timestamp is *older* than registrar.m.stamp's timestamp and doesn't run again. We only use the stamp file for source code (registrar.[m|h], main.[m|h], pinvokes.[m|h]), since using it every time has too much potential for running into other problems (for instance we should never create .stamp files inside the .app). Fixes these test failures: 1) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single","",False,System.String[]) single Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory371/testApp.app/testApp is modified, timestamp: 2/15/2018 3:04:11 PM > 2/15/2018 3:04:09 PM" > 2) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("dual","armv7,arm64",False,System.String[]) dual Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory375/testApp.app/testApp is modified, timestamp: 2/15/2018 3:06:03 PM > 2/15/2018 3:06:00 PM" > 3) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("llvm","armv7+llvm",False,System.String[]) llvm Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory379/testApp.app/testApp is modified, timestamp: 2/15/2018 3:07:14 PM > 2/15/2018 3:07:12 PM" > 4) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("debug","",True,System.String[]) debug Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory383/testApp.app/testApp is modified, timestamp: 2/15/2018 3:08:16 PM > 2/15/2018 3:08:13 PM" > 5) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single-framework","",False,System.String[]) single-framework Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory387/testApp.app/testApp is modified, timestamp: 2/15/2018 3:09:18 PM > 2/15/2018 3:09:16 PM" > Fixes https://github.com/xamarin/maccore/issues/641
2018-02-19 22:28:04 +03:00
public static bool IsUptodate (string source, string target, bool check_contents = false, bool check_stamp = true)
2016-04-21 15:57:02 +03:00
{
return FileCopier.IsUptodate (source, target, check_contents, check_stamp);
2016-04-21 15:57:02 +03:00
}
public static void RemoveResource (ModuleDefinition module, string name)
{
for (int i = 0; i < module.Resources.Count; i++) {
EmbeddedResource embedded = module.Resources[i] as EmbeddedResource;
if (embedded == null || embedded.Name != name)
continue;
module.Resources.RemoveAt (i);
break;
}
}
public static void SaveAssembly (AssemblyDefinition assembly, string destination)
{
var main = assembly.MainModule;
bool symbols = main.HasSymbols;
2016-04-21 15:57:02 +03:00
if (symbols) {
var provider = new DefaultSymbolReaderProvider ();
main.ReadSymbols (provider.GetSymbolReader (main, main.FileName));
}
var wp = new WriterParameters () {
WriteSymbols = symbols,
SymbolWriterProvider = symbols ? new CustomSymbolWriterProvider () : null,
};
// re-write symbols, if available, so the new tokens will match
assembly.Write (destination, wp);
if (!symbols) {
2016-04-21 15:57:02 +03:00
// if we're not saving the symbols then we must not leave stale/old files to be used by other tools
string dest_mdb = destination + ".mdb";
if (File.Exists (dest_mdb))
File.Delete (dest_mdb);
string dest_pdb = Path.ChangeExtension (destination, ".pdb");
if (File.Exists (dest_pdb))
File.Delete (dest_pdb);
2016-04-21 15:57:02 +03:00
}
}
public static void ExtractResource (ModuleDefinition module, string name, string path, bool remove)
{
for (int i = 0; i < module.Resources.Count; i++) {
EmbeddedResource embedded = module.Resources[i] as EmbeddedResource;
if (embedded == null || embedded.Name != name)
continue;
string dirname = Path.GetDirectoryName (path);
if (!Directory.Exists (dirname))
Directory.CreateDirectory (dirname);
using (Stream ostream = File.OpenWrite (path)) {
embedded.GetResourceStream ().CopyTo (ostream);
}
if (remove)
module.Resources.RemoveAt (i);
break;
}
}
2019-02-01 21:40:50 +03:00
// Returns true if the source file was copied to the target or false if it was already up to date.
public static bool UpdateFile (string source, string target, bool check_contents = false)
2016-04-21 15:57:02 +03:00
{
2019-02-01 21:40:50 +03:00
if (!Application.IsUptodate (source, target, check_contents)) {
2016-04-21 15:57:02 +03:00
CopyFile (source, target);
2019-02-01 21:40:50 +03:00
return true;
}
else {
2016-04-21 15:57:02 +03:00
Driver.Log (3, "Target '{0}' is up-to-date", target);
2019-02-01 21:40:50 +03:00
return false;
}
2016-04-21 15:57:02 +03:00
}
// Checks if any of the source files have a time stamp later than any of the target files.
[mtouch/mmp] Fix tracking of whether the static registrar should run again or not. Fixes #641. (#3534) * [tests] Improve debug spew for the RebuildTest_WithExtensions test. * [mtouch/mmp] Store/load if the dynamic registrar is removed or not into the cached link results. Store/load if the dynamic registrar is removed or not into the cached link results, so that we generate the correct main.m even if cached linker results are used. * [mtouch/mmp] The static registrar must not execute if we're loading cached results from the linker. The static registrar must not execute if we're loading cached results from the linker, because the static registrar needs information from the linker that's not restored from the cache. * [mtouch/mmp] Share Touch code. * [mtouch/mmp] Make it possible to touch inexistent files (to create them). * [mtouch/mmp] Fix tracking of whether the static registrar should run again or not. The recent changes to support optimizing away the dynamic registrar caused the Xamarin.MTouch.RebuildTest_WithExtensions test to regress. The problem ----------- * The linker now collects and stores information the static registrar needs. * This information is not restored from disk when the linker realizes that it can reload previously linked assemblies instead of executing again. * The static registrar runs again (for another reason). * The information the static registrar needs isn't available, and incorrect output follows. So fix 1: show an error if the static registrar runs when the linker loaded cached results. The exact scenario the test ran into is this: * 1st build: everything is new and everything is built. * 2nd build: contents of .exe changes, the linker runs again, the static registrar runs again, but sees that the generated output didn't change, so it doesn't write the new content to disk (this is an optimization to avoid compiling the registrar.m file again unless needed). * 3rd build: only the .exe timestamp changes, the linker sees nothing changes in the contents of the .exe and loads the previously linked assemblies from disk, the static registrar sees that the .exe's timestamp is newer than registrar.m's timestamp and run again, but doesn't produce the right result because it doesn't have the information it needs. Considered solutions -------------------- 1. Only track timestamps, not file contents. This is not ideal, since it will result in more work done: in particular for the case above, it would add a registrar.m compilation in build #2, and linker rerun + static registrar rerun + registrar.m compilation + final native link in build #3. 2. Always write the output of the static registrar, even if it hasn't changed. This is not ideal either, since it will also result in more work done: for the case above, it would add a registrar.m compilation + final native link in build #3. 3. Always write the output of the static registrar, but track if it changed or not, and if it didn't, just touch registrar.o instead of recompiling it. This only means the final native link in build #3 is added (see #5 for why this is worse than it sounds). 4. Always write the output of the static registrar, but track it it changed or not, and if it didn't, just touch registrar.o instead of recompiling it, and track that too, so that the final native link in build #3 isn't needed anymore. Unfortunately this may result in incorrect behavior, because now the msbuild tasks will detect that the executable has changed, and may run dsymutil + strip again. The executable didn't actually change, which means it would be the previously stripped executable, and thus we'd end up with an empty .dSYM because we ran dsymtil on an already stripped executable. 5. Idea #4, but write the output of the final link into a temporary directory instead of the .app, so that we could track whether we should update the executable in the .app or not. This is not optimal either, because executables can be *big* (I've seen multi-GB tvOS bitcode executables), and extra copies of such files should not be taken lightly. 6. Idea #4, but tell the MSBuild tasks that dsymutil/strip doesn't need to be rerun even if the timestamp of the executable changed. This might actually work, but now the solution's become quite complex. Implemented solution -------------------- Use stamp files to detect whether a file is up-to-date or not. In particular: * When we don't write to a file because the new contents are identical to the old contents, we now touch a .stamp file. This stamp file means "the accompanying file was determined to be up-to-date when the stamp was touched." * When checking whether a file is up-to-date, also check for the presence of a .stamp file, and if it exists, use the highest timestamp between the stamp file and the actual file. Now the test scenario becomes: * 1st build: everything is new and everything is built. * 2nd build: contents of .exe changes, the linker runs again, the static registrar runs again, but sees that the generated output didn't change, so it doesn't write the new content to disk, but it creates a registrar.m.stamp file to indicate the point in time when registrar.m was considered up-to- date. * 3rd build: only the .exe timestamp changes, the linker sees nothing changes in the contents of the .exe and loads the previously linked assemblies from disk, the static registrar sees that the .exe's timestamp is *older* than registrar.m.stamp's timestamp and doesn't run again. We only use the stamp file for source code (registrar.[m|h], main.[m|h], pinvokes.[m|h]), since using it every time has too much potential for running into other problems (for instance we should never create .stamp files inside the .app). Fixes these test failures: 1) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single","",False,System.String[]) single Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory371/testApp.app/testApp is modified, timestamp: 2/15/2018 3:04:11 PM > 2/15/2018 3:04:09 PM" > 2) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("dual","armv7,arm64",False,System.String[]) dual Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory375/testApp.app/testApp is modified, timestamp: 2/15/2018 3:06:03 PM > 2/15/2018 3:06:00 PM" > 3) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("llvm","armv7+llvm",False,System.String[]) llvm Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory379/testApp.app/testApp is modified, timestamp: 2/15/2018 3:07:14 PM > 2/15/2018 3:07:12 PM" > 4) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("debug","",True,System.String[]) debug Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory383/testApp.app/testApp is modified, timestamp: 2/15/2018 3:08:16 PM > 2/15/2018 3:08:13 PM" > 5) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single-framework","",False,System.String[]) single-framework Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory387/testApp.app/testApp is modified, timestamp: 2/15/2018 3:09:18 PM > 2/15/2018 3:09:16 PM" > Fixes https://github.com/xamarin/maccore/issues/641
2018-02-19 22:28:04 +03:00
//
// If check_stamp is true, the function will use the timestamp of a "target".stamp file
// if it's later than the timestamp of the "target" file itself.
public static bool IsUptodate (IEnumerable<string> sources, IEnumerable<string> targets, bool check_stamp = true)
2016-04-21 15:57:02 +03:00
{
if (Driver.Force)
return false;
DateTime max_source = DateTime.MinValue;
string max_s = null;
if (sources.Count () == 0 || targets.Count () == 0)
throw ErrorHelper.CreateError (1013, Errors.MT1013);
2016-04-21 15:57:02 +03:00
foreach (var s in sources) {
var sfi = new FileInfo (s);
if (!sfi.Exists) {
Driver.Log (3, "Prerequisite '{0}' does not exist.", s);
return false;
}
var st = sfi.LastWriteTimeUtc;
if (st > max_source) {
max_source = st;
max_s = s;
}
}
foreach (var t in targets) {
var tfi = new FileInfo (t);
if (!tfi.Exists) {
Driver.Log (3, "Target '{0}' does not exist.", t);
return false;
}
[mtouch/mmp] Fix tracking of whether the static registrar should run again or not. Fixes #641. (#3534) * [tests] Improve debug spew for the RebuildTest_WithExtensions test. * [mtouch/mmp] Store/load if the dynamic registrar is removed or not into the cached link results. Store/load if the dynamic registrar is removed or not into the cached link results, so that we generate the correct main.m even if cached linker results are used. * [mtouch/mmp] The static registrar must not execute if we're loading cached results from the linker. The static registrar must not execute if we're loading cached results from the linker, because the static registrar needs information from the linker that's not restored from the cache. * [mtouch/mmp] Share Touch code. * [mtouch/mmp] Make it possible to touch inexistent files (to create them). * [mtouch/mmp] Fix tracking of whether the static registrar should run again or not. The recent changes to support optimizing away the dynamic registrar caused the Xamarin.MTouch.RebuildTest_WithExtensions test to regress. The problem ----------- * The linker now collects and stores information the static registrar needs. * This information is not restored from disk when the linker realizes that it can reload previously linked assemblies instead of executing again. * The static registrar runs again (for another reason). * The information the static registrar needs isn't available, and incorrect output follows. So fix 1: show an error if the static registrar runs when the linker loaded cached results. The exact scenario the test ran into is this: * 1st build: everything is new and everything is built. * 2nd build: contents of .exe changes, the linker runs again, the static registrar runs again, but sees that the generated output didn't change, so it doesn't write the new content to disk (this is an optimization to avoid compiling the registrar.m file again unless needed). * 3rd build: only the .exe timestamp changes, the linker sees nothing changes in the contents of the .exe and loads the previously linked assemblies from disk, the static registrar sees that the .exe's timestamp is newer than registrar.m's timestamp and run again, but doesn't produce the right result because it doesn't have the information it needs. Considered solutions -------------------- 1. Only track timestamps, not file contents. This is not ideal, since it will result in more work done: in particular for the case above, it would add a registrar.m compilation in build #2, and linker rerun + static registrar rerun + registrar.m compilation + final native link in build #3. 2. Always write the output of the static registrar, even if it hasn't changed. This is not ideal either, since it will also result in more work done: for the case above, it would add a registrar.m compilation + final native link in build #3. 3. Always write the output of the static registrar, but track if it changed or not, and if it didn't, just touch registrar.o instead of recompiling it. This only means the final native link in build #3 is added (see #5 for why this is worse than it sounds). 4. Always write the output of the static registrar, but track it it changed or not, and if it didn't, just touch registrar.o instead of recompiling it, and track that too, so that the final native link in build #3 isn't needed anymore. Unfortunately this may result in incorrect behavior, because now the msbuild tasks will detect that the executable has changed, and may run dsymutil + strip again. The executable didn't actually change, which means it would be the previously stripped executable, and thus we'd end up with an empty .dSYM because we ran dsymtil on an already stripped executable. 5. Idea #4, but write the output of the final link into a temporary directory instead of the .app, so that we could track whether we should update the executable in the .app or not. This is not optimal either, because executables can be *big* (I've seen multi-GB tvOS bitcode executables), and extra copies of such files should not be taken lightly. 6. Idea #4, but tell the MSBuild tasks that dsymutil/strip doesn't need to be rerun even if the timestamp of the executable changed. This might actually work, but now the solution's become quite complex. Implemented solution -------------------- Use stamp files to detect whether a file is up-to-date or not. In particular: * When we don't write to a file because the new contents are identical to the old contents, we now touch a .stamp file. This stamp file means "the accompanying file was determined to be up-to-date when the stamp was touched." * When checking whether a file is up-to-date, also check for the presence of a .stamp file, and if it exists, use the highest timestamp between the stamp file and the actual file. Now the test scenario becomes: * 1st build: everything is new and everything is built. * 2nd build: contents of .exe changes, the linker runs again, the static registrar runs again, but sees that the generated output didn't change, so it doesn't write the new content to disk, but it creates a registrar.m.stamp file to indicate the point in time when registrar.m was considered up-to- date. * 3rd build: only the .exe timestamp changes, the linker sees nothing changes in the contents of the .exe and loads the previously linked assemblies from disk, the static registrar sees that the .exe's timestamp is *older* than registrar.m.stamp's timestamp and doesn't run again. We only use the stamp file for source code (registrar.[m|h], main.[m|h], pinvokes.[m|h]), since using it every time has too much potential for running into other problems (for instance we should never create .stamp files inside the .app). Fixes these test failures: 1) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single","",False,System.String[]) single Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory371/testApp.app/testApp is modified, timestamp: 2/15/2018 3:04:11 PM > 2/15/2018 3:04:09 PM" > 2) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("dual","armv7,arm64",False,System.String[]) dual Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory375/testApp.app/testApp is modified, timestamp: 2/15/2018 3:06:03 PM > 2/15/2018 3:06:00 PM" > 3) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("llvm","armv7+llvm",False,System.String[]) llvm Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory379/testApp.app/testApp is modified, timestamp: 2/15/2018 3:07:14 PM > 2/15/2018 3:07:12 PM" > 4) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("debug","",True,System.String[]) debug Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory383/testApp.app/testApp is modified, timestamp: 2/15/2018 3:08:16 PM > 2/15/2018 3:08:13 PM" > 5) Failed : Xamarin.MTouch.RebuildTest_WithExtensions("single-framework","",False,System.String[]) single-framework Expected: <empty> But was: < "/Users/builder/data/lanes/5746/4123bf7e/source/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory387/testApp.app/testApp is modified, timestamp: 2/15/2018 3:09:18 PM > 2/15/2018 3:09:16 PM" > Fixes https://github.com/xamarin/maccore/issues/641
2018-02-19 22:28:04 +03:00
if (check_stamp) {
var tfi_stamp = new FileInfo (t + ".stamp");
if (tfi_stamp.Exists && tfi_stamp.LastWriteTimeUtc > tfi.LastWriteTimeUtc) {
Driver.Log (3, "Target '{0}' has a stamp file with newer timestamp ({1} > {2}), using the stamp file's timestamp", t, tfi_stamp.LastWriteTimeUtc, tfi.LastWriteTimeUtc);
tfi = tfi_stamp;
}
}
2016-04-21 15:57:02 +03:00
var lwt = tfi.LastWriteTimeUtc;
if (max_source > lwt) {
Driver.Log (3, "Prerequisite '{0}' is newer than target '{1}' ({2} vs {3}).", max_s, t, max_source, lwt);
return false;
}
}
Driver.Log (3, "Prerequisite(s) '{0}' are all older than the target(s) '{1}'.", string.Join ("', '", sources.ToArray ()), string.Join ("', '", targets.ToArray ()));
return true;
}
public static void UpdateDirectory (string source, string target)
{
FileCopier.UpdateDirectory (source, target);
}
2016-04-21 15:57:02 +03:00
[mtouch][mmp] Exclude extraneous framework files from being copied into apps (#10441) Today both `mtouch` and `mmp` are copying the entire `.framework` directories inside the `.app\[Contents\]Frameworks\` directory. However not everything in a framework is required at runtime. The most common unrequired files would be headers (`Headers/*.h`) and modules (`Modules/*`). Looking at Xcode build output we can see something like: ``` builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -exclude Headers -exclude PrivateHeaders -exclude Modules -exclude \*.tbd -bitcode-strip replace-with-marker -bitcode-strip-tool ``` which excludes a few more, less common, files. This _builtin_ command is not available externally (for us to re-use) but it hints that Xcode is likely using `rsync` to avoid copying part of the files. Note: the builtin command also _likely_ calls `bitcode_strip` too (or has similar code embedded) and `mtouch` already does so too There's a cost to spawning an external process, like `rsync`, which we avoid by having our own file copier, which clones files (almost zero cost). That does not support excluding files, but deleting files is also very cheap. Testing shows copying a framework to be less than 1 ms, even with with extra deletion step. * Tweak `GetRealPath` to optionally not to warn if the path does not exists since, in this case, it's a check we want to do after resolving the path This fixes several (5) MTouch tests looking for specific (and no extra) warnings ``` Unable to canonicalize the path '/Users/builder/azdo/_work/2/s/xamarin-macios/tests/mtouch/bin/Debug/tmp-test-dir/Xamarin.Tests.BundlerTool.CreateTemporaryDirectory195/testApp.app/Frameworks/Mono.framework/CVS': No such file or directory (2). ```
2021-01-19 16:48:10 +03:00
static string[] NonEssentialDirectoriesInsideFrameworks = { "CVS", ".svn", ".git", ".hg", "Headers", "PrivateHeaders", "Modules" };
// Duplicate xcode's `builtin-copy` exclusions
public static void ExcludeNonEssentialFrameworkFiles (string framework)
{
// builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -exclude Headers -exclude PrivateHeaders -exclude Modules -exclude \*.tbd
File.Delete (Path.Combine (framework, ".DS_Store"));
File.Delete (Path.Combine (framework, "*.tbd"));
foreach (var dir in NonEssentialDirectoriesInsideFrameworks)
DeleteDir (Path.Combine (framework, dir));
}
static void DeleteDir (string dir)
{
// Xcode generates symlinks inside macOS frameworks
var realdir = Target.GetRealPath (dir, warnIfNoSuchPathExists: false);
// unlike File.Delete this would throw if the directory does not exists
if (Directory.Exists (realdir)) {
Directory.Delete (realdir, true);
if (realdir != dir)
File.Delete (dir); // because a symlink is a file :)
}
}
2016-04-21 15:57:02 +03:00
[DllImport (Constants.libSystemLibrary)]
static extern int readlink (string path, IntPtr buf, int len);
// A file copy that will replace symlinks with the source file
// File.Copy will copy the source to the target of the symlink instead
// of replacing the symlink.
public static void CopyFile (string source, string target)
{
if (readlink (target, IntPtr.Zero, 0) != -1) {
// Target is a symlink, delete it.
File.Delete (target);
} else if (File.Exists (target)) {
// Also delete the target file if it already exists,
// since it may not have write permissions.
File.Delete (target);
}
var dir = Path.GetDirectoryName (target);
if (!Directory.Exists (dir))
Directory.CreateDirectory (dir);
File.Copy (source, target, true);
// Make sure the target file is r/w.
var attrs = File.GetAttributes (target);
if ((attrs & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
File.SetAttributes (target, attrs & ~FileAttributes.ReadOnly);
Driver.Log (1, "Copied {0} to {1}", source, target);
}
public void InitializeCommon ()
{
InitializeDeploymentTarget ();
SelectRegistrar ();
SelectMonoNative ();
RuntimeOptions = RuntimeOptions.Create (this, HttpMessageHandler, TlsProvider);
if (Platform == ApplePlatform.MacCatalyst) {
// Our input SdkVersion is the macOS SDK version, but the rest of our code expects the supporting iOS version, so convert here.
// The macOS SDK version is still stored in NativeSdkVersion for when we need it.
SdkVersion = GetMacCatalystiOSVersion (NativeSdkVersion);
}
if (RequiresXcodeHeaders && SdkVersion < SdkVersions.GetVersion (this)) {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
case ApplePlatform.MacCatalyst:
throw ErrorHelper.CreateError (180, Errors.MX0180, ProductName, PlatformName, SdkVersions.GetVersion (this), SdkVersions.Xcode);
case ApplePlatform.MacOSX:
throw ErrorHelper.CreateError (179, Errors.MX0179, ProductName, PlatformName, SdkVersions.GetVersion (this), SdkVersions.Xcode);
default:
// Default to the iOS error message, it's better than showing MX0071 (unknown platform), which would be completely unrelated
goto case ApplePlatform.iOS;
}
}
if (Platform == ApplePlatform.WatchOS && EnableCoopGC.HasValue && !EnableCoopGC.Value)
2020-01-31 23:02:52 +03:00
throw ErrorHelper.CreateError (88, Errors.MT0088);
if (!EnableCoopGC.HasValue)
EnableCoopGC = Platform == ApplePlatform.WatchOS;
SetObjectiveCExceptionMode ();
SetManagedExceptionMode ();
[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 (SymbolMode == SymbolMode.Default) {
#if MONOTOUCH
SymbolMode = EnableBitCode ? SymbolMode.Code : SymbolMode.Linker;
#else
SymbolMode = SymbolMode.Linker;
#endif
}
#if MONOTOUCH
if (EnableBitCode && SymbolMode != SymbolMode.Code) {
// This is a warning because:
// * The user will get a linker error anyway if they do this.
// * I see it as quite unlikely that anybody will in fact try this (it must be manually set in the additional mtouch arguments).
// * I find it more probable that Apple will remove the -u restriction, in which case someone might actually want to try this, and if it's a warning, we won't prevent it.
2020-01-31 23:02:52 +03:00
ErrorHelper.Warning (115, Errors.MT0115);
[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
}
#endif
[mtouch/mmp] Give users more control over optimizations, and share more code between mtouch and mmp. (#3242) * [mtouch/mmp] Give users more control over optimizations, and share more code between mtouch and mmp. 1. Add an --optimize flag to mtouch/mmp that allows users to select which optimizations to apply (or not). This makes it easier to add future optimizations, and allow users to disable any optimization that causes problems without having to disable many other features. 2. Share as much optimization code as possible between mtouch and mmp. This immediately gives a benefit to mmp, which has three new optimizations only mtouch had: NSObject.IsDirectBinding inlining, IntPtr.Size inlining and dead code elimination. This results in ~6kb of disk space saved for a linked Xamarin.Mac app: * link sdk: [Debug][1], [Release][2] * link all: [Debug][3], [Release][4] Testing also verifies that monotouchtest ([Debug][5], [Release][6]) has not changed size at all, which means that no default optimizations have changed inadvertedly. [1]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-sdk-mac--debug [2]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-sdk-mac--release [3]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-all-mac--debug [4]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#link-all-mac--release [5]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#monotouchtest-iphonedebug64 [6]: https://gist.github.com/rolfbjarne/6b731e3b5ca6170355662e6505c3d492#monotouchtest-iphonerelease64 * [tools] Don't enable the IsDirectBinding optimization by default for Xamarin.Mac apps, it's not safe. * Fix whitespace issues. * [doc] Document optimizations. * Officially support optimizations by adding them to the Versions.plist. * [linker] Improve IntPtr.Size inliner + dead code eliminatior and add tests. * Properly handle operands for the ldc_i4_s instruction (they're sbyte). * Fix less-than condition to actually do a less-than comparison. * Make sure to look up the bitness in the Target, not the Application, since the Application's value will be incorrect when building fat apps (both Is32Build and Is64Build will be true). * Remove unnecessary checks for the IntPtr.Size inliner: this optimization does not depend on other instructions than the IntPtr.get_Size call, so remove the checks that verify surrounding instructions. This makes the IntPtr.Size inliner kick in in more scenarios (such as the new tests). * Add tests. * [tests] Add mmp tests for optimizations. * [tests] Fix XM optimization tests. * [tests] Fix test build error.
2018-01-23 13:33:48 +03:00
if (!DebugTrack.HasValue) {
DebugTrack = false;
} else if (DebugTrack.Value && !EnableDebug) {
ErrorHelper.Warning (32, Errors.MT0032);
}
if (!package_managed_debug_symbols.HasValue) {
package_managed_debug_symbols = EnableDebug;
} else if (package_managed_debug_symbols.Value && IsLLVM) {
ErrorHelper.Warning (3007, Errors.MX3007);
}
Optimizations.Initialize (this, out var messages);
ErrorHelper.Show (messages);
if (Driver.Verbosity > 3)
Driver.Log (4, $"Enabled optimizations: {Optimizations}");
}
void InitializeDeploymentTarget ()
{
#if ENABLE_BITCODE_ON_IOS
if (Platform == ApplePlatform.iOS)
DeploymentTarget = new Version (9, 0);
#endif
if (DeploymentTarget == null)
DeploymentTarget = SdkVersions.GetVersion (this);
if (Platform == ApplePlatform.iOS && (HasDynamicLibraries || HasFrameworks) && DeploymentTarget.Major < 8) {
ErrorHelper.Warning (78, Errors.MT0078, DeploymentTarget);
DeploymentTarget = new Version (8, 0);
}
if (DeploymentTarget != null) {
if (DeploymentTarget < SdkVersions.GetMinVersion (this))
throw new ProductException (73, true, Errors.MT0073, ProductConstants.Version, DeploymentTarget, Xamarin.SdkVersions.GetMinVersion (this), PlatformName, ProductName);
if (DeploymentTarget > SdkVersions.GetVersion (this))
throw new ProductException (74, true, Errors.MX0074, ProductConstants.Version, DeploymentTarget, Xamarin.SdkVersions.GetVersion (this), PlatformName, ProductName);
}
}
void SelectMonoNative ()
{
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
MonoNativeMode = DeploymentTarget.Major >= 10 ? MonoNativeMode.Unified : MonoNativeMode.Compat;
break;
case ApplePlatform.WatchOS:
if (IsArchEnabled (Abis, Abi.ARM64_32)) {
MonoNativeMode = MonoNativeMode.Unified;
} else {
MonoNativeMode = DeploymentTarget.Major >= 3 ? MonoNativeMode.Unified : MonoNativeMode.Compat;
}
break;
case ApplePlatform.MacOSX:
if (DeploymentTarget >= new Version (10, 12))
MonoNativeMode = MonoNativeMode.Unified;
else
MonoNativeMode = MonoNativeMode.Compat;
break;
case ApplePlatform.MacCatalyst:
MonoNativeMode = MonoNativeMode.Unified;
break;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
public string GetLibNativeName ()
{
switch (MonoNativeMode) {
case MonoNativeMode.Unified:
if (Platform == ApplePlatform.MacCatalyst)
return "libmono-native";
return "libmono-native-unified";
case MonoNativeMode.Compat:
return "libmono-native-compat";
default:
throw ErrorHelper.CreateError (99, Errors.MX0099, $"Invalid mono native type: '{MonoNativeMode}'");
}
}
public void RunRegistrar ()
{
// The static registrar.
if (Registrar != RegistrarMode.Static)
throw new ProductException (67, Errors.MT0067, Registrar); // this is only called during our own build
if (RootAssemblies.Count < 1)
2020-01-31 23:02:52 +03:00
throw ErrorHelper.CreateError (130, Errors.MX0130);
var registrar_m = RegistrarOutputLibrary;
var RootAssembly = RootAssemblies [0];
var resolvedAssemblies = new Dictionary<string, AssemblyDefinition> ();
var resolver = new PlatformResolver () {
RootDirectory = Path.GetDirectoryName (RootAssembly),
#if MMP
CommandLineAssemblies = RootAssemblies,
#endif
};
if (Platform == ApplePlatform.iOS && !Driver.IsDotNet) {
if (Is32Build) {
resolver.ArchDirectory = Driver.GetArch32Directory (this);
} else {
resolver.ArchDirectory = Driver.GetArch64Directory (this);
}
}
var ps = new ReaderParameters ();
ps.AssemblyResolver = resolver;
foreach (var reference in References) {
var r = resolver.Load (reference);
if (r == null)
throw ErrorHelper.CreateError (2002, Errors.MT2002, reference);
}
var productAssembly = Driver.GetProductAssembly (this);
bool foundProductAssembly = false;
foreach (var asm in RootAssemblies) {
var rootName = Path.GetFileNameWithoutExtension (asm);
if (rootName == productAssembly)
foundProductAssembly = true;
try {
AssemblyDefinition lastAssembly = ps.AssemblyResolver.Resolve (AssemblyNameReference.Parse (rootName), new ReaderParameters ());
if (lastAssembly == null) {
2020-01-31 23:02:52 +03:00
ErrorHelper.CreateWarning (7, Errors.MX0007, rootName);
continue;
}
if (resolvedAssemblies.TryGetValue (rootName, out var previousAssembly)) {
if (lastAssembly.MainModule.RuntimeVersion != previousAssembly.MainModule.RuntimeVersion) {
Driver.Log (2, "Attemping to load an assembly another time {0} (previous {1})", lastAssembly.FullName, previousAssembly.FullName);
}
continue;
}
resolvedAssemblies.Add (rootName, lastAssembly);
Driver.Log (3, "Loaded {0}", lastAssembly.MainModule.FileName);
} catch (Exception ex) {
2020-01-31 23:02:52 +03:00
ErrorHelper.Warning (9, ex, Errors.MX0009, $"{rootName}: {ex.Message}");
continue;
}
}
if (!foundProductAssembly)
2020-01-31 23:02:52 +03:00
throw ErrorHelper.CreateError (131, Errors.MX0131, productAssembly, string.Join ("', '", RootAssemblies.ToArray ()));
#if MONOTOUCH
if (SelectAbis (Abis, Abi.SimulatorArchMask).Count > 0) {
BuildTarget = BuildTarget.Simulator;
} else if (SelectAbis (Abis, Abi.DeviceArchMask).Count > 0) {
BuildTarget = BuildTarget.Device;
} else {
throw ErrorHelper.CreateError (99, Errors.MX0099, "No valid ABI");
}
#endif
var registrar = new Registrar.StaticRegistrar (this);
if (RootAssemblies.Count == 1)
registrar.GenerateSingleAssembly (resolver, resolvedAssemblies.Values, Path.ChangeExtension (registrar_m, "h"), registrar_m, Path.GetFileNameWithoutExtension (RootAssembly));
else
registrar.Generate (resolvedAssemblies.Values, Path.ChangeExtension (registrar_m, "h"), registrar_m);
}
2020-05-11 17:27:19 +03:00
public IEnumerable<Abi> Abis {
get { return abis; }
set { abis = new List<Abi> (value); }
2020-05-11 17:27:19 +03:00
}
public bool IsArchEnabled (Abi arch)
{
return IsArchEnabled (abis, arch);
}
public static bool IsArchEnabled (IEnumerable<Abi> abis, Abi arch)
{
foreach (var abi in abis) {
if ((abi & arch) != 0)
return true;
}
return false;
}
public void SetDefaultAbi ()
{
if (abis == null)
abis = new List<Abi> ();
switch (Platform) {
case ApplePlatform.iOS:
if (abis.Count == 0) {
if (DeploymentTarget == null || DeploymentTarget.Major >= 11) {
abis.Add (IsDeviceBuild ? Abi.ARM64 : Abi.x86_64);
} else {
abis.Add (IsDeviceBuild ? Abi.ARMv7 : Abi.i386);
}
}
break;
case ApplePlatform.WatchOS:
if (abis.Count == 0)
throw ErrorHelper.CreateError (76, Errors.MT0076, "Xamarin.WatchOS");
break;
case ApplePlatform.TVOS:
if (abis.Count == 0)
throw ErrorHelper.CreateError (76, Errors.MT0076, "Xamarin.TVOS");
break;
case ApplePlatform.MacOSX:
if (abis.Count == 0)
abis.Add (Abi.x86_64);
break;
case ApplePlatform.MacCatalyst:
if (abis.Count == 0)
throw ErrorHelper.CreateError (76, Errors.MT0076, "Xamarin.MacCatalyst");
break;
2020-05-11 17:27:19 +03:00
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
public void ValidateAbi ()
{
var validAbis = new List<Abi> ();
switch (Platform) {
case ApplePlatform.iOS:
if (IsDeviceBuild) {
validAbis.Add (Abi.ARMv7);
validAbis.Add (Abi.ARMv7 | Abi.Thumb);
validAbis.Add (Abi.ARMv7 | Abi.LLVM);
validAbis.Add (Abi.ARMv7 | Abi.LLVM | Abi.Thumb);
validAbis.Add (Abi.ARMv7s);
validAbis.Add (Abi.ARMv7s | Abi.Thumb);
validAbis.Add (Abi.ARMv7s | Abi.LLVM);
validAbis.Add (Abi.ARMv7s | Abi.LLVM | Abi.Thumb);
} else {
validAbis.Add (Abi.i386);
}
if (IsDeviceBuild) {
validAbis.Add (Abi.ARM64);
validAbis.Add (Abi.ARM64 | Abi.LLVM);
} else {
validAbis.Add (Abi.x86_64);
}
break;
case ApplePlatform.WatchOS:
if (IsDeviceBuild) {
validAbis.Add (Abi.ARMv7k);
validAbis.Add (Abi.ARMv7k | Abi.LLVM);
validAbis.Add (Abi.ARM64_32);
validAbis.Add (Abi.ARM64_32 | Abi.LLVM);
} else {
validAbis.Add (Abi.i386);
validAbis.Add (Abi.x86_64);
2020-05-11 17:27:19 +03:00
}
break;
case ApplePlatform.TVOS:
if (IsDeviceBuild) {
validAbis.Add (Abi.ARM64);
validAbis.Add (Abi.ARM64 | Abi.LLVM);
} else {
validAbis.Add (Abi.x86_64);
}
break;
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
2020-05-11 17:27:19 +03:00
validAbis.Add (Abi.x86_64);
Xamarin.Mac native Apple Silicon targetting support (#10115) * Add support for Xamarin.Mac arm64 * Add compile product definition task Xamarin.Mac can be provided with a ProductDefinition file for the generated pkg. Normally, providing a product definition was optional. However, with Apple Silicon, we have an extra issue : `productbuild` needs to know what architectures your package target. If not provided with them, it will guess to the best of its abilities. However, on Catalina and lower, the guess is x86_64, even if you have an arm64 slice. To fix this, we add a new task to compile the product definition and use this file to create the pkg. If you provide your own Product Definition, we can check and warn if the architectures don't match what we expect. If the file doesn't exist or there is no architecture, we set it ourselves based on our target architectures. * Don't reference dynamic objC_send on arm64 When building in debug, we currently try to link dynamic objC_send symbols when targeting a 64-bit architecture. However, this is actually only defined on Intel architectures, not on arm64, so we end up failing because we're referring symbols that don't exist. Rework the `GetRequiredSymbols` to take an abi, and tag those symbols to only be valid on i386/x86_64, so they don't get referred at all when building on arm64, but still get referred in x86_64. * Fix improper delete/move with already existing directories * Fix stret requirement for Xamarin.Mac in arm64. The generator supposes that we're running in x64 mode, refactor to take into account the possibility of running in arm64. * Implement OS version generation in Product.plist, based on MinimumSystemVersion of the app * Re-generalize some mmp registrar rules `Microsoft.macOS.registrar` was missed by the current rule set * Fix mmp tests * Set E7072 as not translated Tests were failing otherwise * Rename Xamarin.Mac lib/x86_64 folder to 64bits (currently all targeted archs are the same) * Fix style issues * Fix `ToLower` usage for invariant usage * Fix xtro-sharpie test
2021-03-18 04:48:02 +03:00
validAbis.Add (Abi.ARM64);
2020-05-11 17:27:19 +03:00
break;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
#if MMP
// This is technically not needed, because we'll fail the validation just below, but this handles
// a common case (existing 32-bit projects) and shows a better error message.
if (abis.Count == 1 && abis [0] == Abi.i386)
throw ErrorHelper.CreateError (144, Errors.MM0144);
#endif
foreach (var abi in abis) {
if (!validAbis.Contains (abi))
throw ErrorHelper.CreateError (75, Errors.MT0075, abi, Platform, string.Join (", ", validAbis.Select ((v) => v.AsString ()).ToArray ()));
}
}
public void ClearAbi ()
{
abis = null;
}
public void ParseAbi (string abi)
{
var res = new List<Abi> ();
foreach (var str in abi.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) {
Abi value;
switch (str) {
case "i386":
value = Abi.i386;
break;
case "x86_64":
value = Abi.x86_64;
break;
case "armv7":
value = Abi.ARMv7;
break;
case "armv7+llvm":
value = Abi.ARMv7 | Abi.LLVM;
break;
case "armv7+llvm+thumb2":
value = Abi.ARMv7 | Abi.LLVM | Abi.Thumb;
break;
case "armv7s":
value = Abi.ARMv7s;
break;
case "armv7s+llvm":
value = Abi.ARMv7s | Abi.LLVM;
break;
case "armv7s+llvm+thumb2":
value = Abi.ARMv7s | Abi.LLVM | Abi.Thumb;
break;
case "arm64":
value = Abi.ARM64;
break;
case "arm64+llvm":
value = Abi.ARM64 | Abi.LLVM;
break;
case "arm64_32":
value = Abi.ARM64_32;
break;
case "arm64_32+llvm":
value = Abi.ARM64_32 | Abi.LLVM;
break;
case "armv7k":
value = Abi.ARMv7k;
break;
case "armv7k+llvm":
value = Abi.ARMv7k | Abi.LLVM;
break;
default:
throw ErrorHelper.CreateError (15, Errors.MT0015, str);
}
// merge this value with any existing ARMv? already specified.
// this is so that things like '--armv7 --thumb' work correctly.
if (abis != null) {
for (int i = 0; i < abis.Count; i++) {
if ((abis [i] & Abi.ArchMask) == (value & Abi.ArchMask)) {
value |= abis [i];
break;
}
}
}
res.Add (value);
}
// We replace any existing abis, to keep the old behavior where '--armv6 --armv7' would
// enable only the last abi specified and disable the rest.
abis = res;
}
public void ParseRegistrar (string v)
{
var split = v.Split ('=');
var name = split [0];
var value = split.Length > 1 ? split [1] : string.Empty;
switch (name) {
case "static":
Registrar = RegistrarMode.Static;
break;
case "dynamic":
Registrar = RegistrarMode.Dynamic;
break;
case "default":
Registrar = RegistrarMode.Default;
break;
#if !MTOUCH
case "partial":
case "partial-static":
Registrar = RegistrarMode.PartialStatic;
break;
#endif
default:
throw ErrorHelper.CreateError (20, Errors.MX0020, "--registrar", "static, dynamic or default");
}
switch (value) {
case "trace":
RegistrarOptions = RegistrarOptions.Trace;
break;
case "default":
case "":
RegistrarOptions = RegistrarOptions.Default;
break;
default:
throw ErrorHelper.CreateError (20, Errors.MX0020, "--registrar", "static, dynamic or default");
}
}
2020-05-11 17:27:19 +03:00
public static string GetArchitectures (IEnumerable<Abi> abis)
{
var res = new List<string> ();
foreach (var abi in abis)
res.Add (abi.AsArchString ());
return string.Join (", ", res.ToArray ());
}
public string MonoGCParams {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
// Configure sgen to use a small nursery
string ret = "nursery-size=512k";
if (IsTodayExtension || Platform == ApplePlatform.WatchOS) {
// A bit test shows different behavior
// Sometimes apps are killed with ~100mb allocated,
// but I've seen apps allocate up to 240+mb as well
ret += ",soft-heap-limit=8m";
}
if (EnableSGenConc)
ret += ",major=marksweep-conc";
else
ret += ",major=marksweep";
return ret;
case ApplePlatform.MacCatalyst:
case ApplePlatform.MacOSX:
return EnableSGenConc ? "major=marksweep-conc" : "major=marksweep";
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
// This is to load the symbols for all assemblies, so that we can give better error messages
// (with file name / line number information).
public void LoadSymbols ()
{
foreach (var t in Targets)
t.LoadSymbols ();
}
public bool IsFrameworkAvailableInSimulator (string framework)
{
if (!Driver.GetFrameworks (this).TryGetValue (framework, out var fw))
return true; // Unknown framework, assume it's valid for the simulator
return fw.IsFrameworkAvailableInSimulator (this);
}
public static bool TryParseManagedExceptionMode (string value, out MarshalManagedExceptionMode mode)
{
mode = MarshalManagedExceptionMode.Default;
switch (value) {
case "default":
mode = MarshalManagedExceptionMode.Default;
break;
case "unwindnative":
case "unwindnativecode":
mode = MarshalManagedExceptionMode.UnwindNativeCode;
break;
case "throwobjectivec":
case "throwobjectivecexception":
mode = MarshalManagedExceptionMode.ThrowObjectiveCException;
break;
case "abort":
mode = MarshalManagedExceptionMode.Abort;
break;
case "disable":
mode = MarshalManagedExceptionMode.Disable;
break;
default:
return false;
}
return true;
}
public static bool TryParseObjectiveCExceptionMode (string value, out MarshalObjectiveCExceptionMode mode)
{
mode = MarshalObjectiveCExceptionMode.Default;
switch (value) {
case "default":
mode = MarshalObjectiveCExceptionMode.Default;
break;
case "unwindmanaged":
case "unwindmanagedcode":
mode = MarshalObjectiveCExceptionMode.UnwindManagedCode;
break;
case "throwmanaged":
case "throwmanagedexception":
mode = MarshalObjectiveCExceptionMode.ThrowManagedException;
break;
case "abort":
mode = MarshalObjectiveCExceptionMode.Abort;
break;
case "disable":
mode = MarshalObjectiveCExceptionMode.Disable;
break;
default:
return false;
}
return true;
}
public void SetManagedExceptionMode ()
{
switch (MarshalManagedExceptions) {
case MarshalManagedExceptionMode.Default:
if (EnableCoopGC.Value) {
MarshalManagedExceptions = MarshalManagedExceptionMode.ThrowObjectiveCException;
} else {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
MarshalManagedExceptions = EnableDebug && IsSimulatorBuild ? MarshalManagedExceptionMode.UnwindNativeCode : MarshalManagedExceptionMode.Disable;
break;
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
MarshalManagedExceptions = EnableDebug ? MarshalManagedExceptionMode.UnwindNativeCode : MarshalManagedExceptionMode.Disable;
break;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071 /* Unknown platform: {0}. This usually indicates a bug in {1}; please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new with a test case. */, Platform, ProductName);
}
}
IsDefaultMarshalManagedExceptionMode = true;
break;
case MarshalManagedExceptionMode.UnwindNativeCode:
case MarshalManagedExceptionMode.Disable:
if (EnableCoopGC.Value)
throw ErrorHelper.CreateError (89, Errors.MT0089, "--marshal-managed-exceptions", MarshalManagedExceptions.ToString ().ToLowerInvariant ());
break;
}
}
public void SetObjectiveCExceptionMode ()
{
switch (MarshalObjectiveCExceptions) {
case MarshalObjectiveCExceptionMode.Default:
if (EnableCoopGC.Value) {
MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.ThrowManagedException;
} else {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
MarshalObjectiveCExceptions = EnableDebug && IsSimulatorBuild ? MarshalObjectiveCExceptionMode.UnwindManagedCode : MarshalObjectiveCExceptionMode.Disable;
break;
case ApplePlatform.MacOSX:
case ApplePlatform.MacCatalyst:
MarshalObjectiveCExceptions = EnableDebug ? MarshalObjectiveCExceptionMode.ThrowManagedException : MarshalObjectiveCExceptionMode.Disable;
break;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071 /* Unknown platform: {0}. This usually indicates a bug in {1}; please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new with a test case. */, Platform, ProductName);
}
}
break;
case MarshalObjectiveCExceptionMode.UnwindManagedCode:
case MarshalObjectiveCExceptionMode.Disable:
if (EnableCoopGC.Value)
throw ErrorHelper.CreateError (89, Errors.MT0089, "--marshal-objectivec-exceptions", MarshalObjectiveCExceptions.ToString ().ToLowerInvariant ());
break;
}
}
2020-10-23 17:52:33 +03:00
// For mobile device builds: returns whether an assembly is interpreted.
// For macOS: N/A
public bool IsInterpreted (string assembly)
{
2020-10-23 17:52:33 +03:00
if (Platform == ApplePlatform.MacOSX)
throw ErrorHelper.CreateError (99, Errors.MX0099, "IsInterpreted isn't a valid operation for macOS apps.");
if (IsSimulatorBuild)
return false;
// IsAOTCompiled and IsInterpreted are not opposites: mscorlib.dll can be both.
if (!UseInterpreter)
return false;
// Go through the list of assemblies to interpret in reverse order,
// so that the last option passed to mtouch takes precedence.
for (int i = InterpretedAssemblies.Count - 1; i >= 0; i--) {
var opt = InterpretedAssemblies [i];
if (opt == "all")
return true;
else if (opt == "-all")
return false;
else if (opt == assembly)
return true;
else if (opt [0] == '-' && opt.Substring (1) == assembly)
return false;
}
// There's an implicit 'all' at the start of the list.
return true;
}
2020-10-23 17:52:33 +03:00
// For mobile device builds: returns whether an assembly is AOT-compiled.
// For macOS: while AOT is supported for macOS, this particular method was not written for macOS, and would need
// revision/testing to be used so desired.
public bool IsAOTCompiled (string assembly)
{
#if NET
if (Platform == ApplePlatform.MacOSX || Platform == ApplePlatform.MacCatalyst)
return false; // AOT on .NET for macOS hasn't been implemented yet.
#else
2020-10-23 17:52:33 +03:00
if (Platform == ApplePlatform.MacOSX)
throw ErrorHelper.CreateError (99, Errors.MX0099, "IsAOTCompiled isn't a valid operation for macOS apps.");
#endif
2020-10-23 17:52:33 +03:00
if (!UseInterpreter)
return true;
// IsAOTCompiled and IsInterpreted are not opposites: mscorlib.dll can be both:
// - mscorlib will always be processed by the AOT compiler to generate required wrapper functions for the interpreter to work
// - mscorlib might also be fully AOT-compiled (both when the interpreter is enabled and when it's not)
if (assembly == Driver.CorlibName)
return true;
return !IsInterpreted (assembly);
}
public IList<string> GetAotArguments (string filename, Abi abi, string outputDir, string outputFile, string llvmOutputFile, string dataFile)
{
GetAotArguments (filename, abi, outputDir, outputFile, llvmOutputFile, dataFile, out var processArguments, out var aotArguments);
processArguments.Add (string.Join (",", aotArguments));
processArguments.Add (filename);
return processArguments;
}
public void GetAotArguments (string filename, Abi abi, string outputDir, string outputFile, string llvmOutputFile, string dataFile, out List<string> processArguments, out List<string> aotArguments)
{
string fname = Path.GetFileName (filename);
processArguments = new List<string> ();
var app = this;
bool enable_llvm = (abi & Abi.LLVM) != 0;
bool enable_thumb = (abi & Abi.Thumb) != 0;
bool enable_debug = app.EnableDebug;
bool enable_debug_symbols = app.PackageManagedDebugSymbols;
bool llvm_only = app.EnableLLVMOnlyBitCode;
bool interp = app.IsInterpreted (Assembly.GetIdentity (filename));
bool interp_full = !interp && app.UseInterpreter;
bool is32bit = (abi & Abi.Arch32Mask) > 0;
string arch = abi.AsArchString ();
processArguments.Add ("--debug");
if (enable_llvm)
processArguments.Add ("--llvm");
if (!llvm_only && !interp)
processArguments.Add ("-O=gsharedvt");
if (app.AotOtherArguments != null)
processArguments.AddRange (app.AotOtherArguments);
aotArguments = new List<string> ();
aotArguments.Add ($"--aot=mtriple={(enable_thumb ? arch.Replace ("arm", "thumb") : arch)}-ios");
aotArguments.Add ($"data-outfile={dataFile}");
aotArguments.AddRange (app.AotArguments.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries));
if (llvm_only)
aotArguments.Add ("llvmonly");
else if (interp) {
if (fname != Driver.CorlibName + ".dll")
throw ErrorHelper.CreateError (99, Errors.MX0099, fname);
aotArguments.Add ("interp");
} else if (interp_full) {
aotArguments.Add ("interp");
aotArguments.Add ("full");
} else
aotArguments.Add ("full");
var aname = Path.GetFileNameWithoutExtension (fname);
var sdk_or_product = Profile.IsSdkAssembly (aname) || Profile.IsProductAssembly (aname);
if (enable_llvm)
aotArguments.Add ("nodebug");
else if (!(enable_debug || enable_debug_symbols))
aotArguments.Add ("nodebug");
else if (app.DebugAll || app.DebugAssemblies.Contains (fname) || !sdk_or_product)
aotArguments.Add ("soft-debug");
aotArguments.Add ("dwarfdebug");
/* Needed for #4587 */
if (enable_debug && !enable_llvm)
aotArguments.Add ("no-direct-calls");
if (!app.UseDlsym (filename))
aotArguments.Add ("direct-pinvoke");
if (app.EnableMSym) {
var msymdir = Path.Combine (outputDir, "Msym");
aotArguments.Add ($"msym-dir={msymdir}");
}
if (enable_llvm)
aotArguments.Add ($"llvm-path={Driver.GetFrameworkCurrentDirectory (app)}/LLVM/bin/");
aotArguments.Add ($"outfile={outputFile}");
if (enable_llvm)
aotArguments.Add ($"llvm-outfile={llvmOutputFile}");
}
public string AssemblyName {
get {
return Path.GetFileName (RootAssemblies [0]);
}
}
internal ProductConstants ProductConstants {
get {
switch (Platform) {
case ApplePlatform.iOS:
case ApplePlatform.MacCatalyst:
return ProductConstants.iOS;
case ApplePlatform.TVOS:
return ProductConstants.tvOS;
case ApplePlatform.WatchOS:
return ProductConstants.watchOS;
case ApplePlatform.MacOSX:
return ProductConstants.macOS;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
}
public void SetDlsymOption (string asm, bool dlsym)
{
if (DlsymAssemblies == null)
DlsymAssemblies = new List<Tuple<string, bool>> ();
DlsymAssemblies.Add (new Tuple<string, bool> (Path.GetFileNameWithoutExtension (asm), dlsym));
DlsymOptions = DlsymOptions.Custom;
}
public void ParseDlsymOptions (string options)
{
bool dlsym;
if (Driver.TryParseBool (options, out dlsym)) {
DlsymOptions = dlsym ? DlsymOptions.All : DlsymOptions.None;
} else {
if (DlsymAssemblies == null)
DlsymAssemblies = new List<Tuple<string, bool>> ();
var assemblies = options.Split (',');
foreach (var assembly in assemblies) {
var asm = assembly;
if (assembly.StartsWith ("+", StringComparison.Ordinal)) {
dlsym = true;
asm = assembly.Substring (1);
} else if (assembly.StartsWith ("-", StringComparison.Ordinal)) {
dlsym = false;
asm = assembly.Substring (1);
} else {
dlsym = true;
}
DlsymAssemblies.Add (new Tuple<string, bool> (Path.GetFileNameWithoutExtension (asm), dlsym));
}
DlsymOptions = DlsymOptions.Custom;
}
}
public bool UseDlsym (string assembly)
{
string asm;
if (DlsymAssemblies != null) {
asm = Path.GetFileNameWithoutExtension (assembly);
foreach (var tuple in DlsymAssemblies) {
if (string.Equals (tuple.Item1, asm, StringComparison.Ordinal))
return tuple.Item2;
}
}
switch (DlsymOptions) {
case DlsymOptions.All:
return true;
case DlsymOptions.None:
return false;
}
if (EnableLLVMOnlyBitCode)
return false;
// Even if this assembly is aot'ed, if we are using the interpreter we can't yet
// guarantee that code in this assembly won't be executed in interpreted mode,
// which can happen for virtual calls between assemblies, during exception handling
// etc. We make sure we don't strip away symbols needed for pinvoke calls.
// https://github.com/mono/mono/issues/14206
if (UseInterpreter)
return true;
#if NET
asm = Path.GetFileNameWithoutExtension (assembly);
switch (asm) {
case "System.Net.Security":
case "System.Net.Quic":
// Some .NET assemblies have P/Invokes to native functions they don't ship. We need to use dlsym for this assemblies.
// https://github.com/dotnet/runtime/issues/47533
return true;
}
#endif
switch (Platform) {
case ApplePlatform.iOS:
return !Profile.IsSdkAssembly (Path.GetFileNameWithoutExtension (assembly));
case ApplePlatform.TVOS:
case ApplePlatform.WatchOS:
case ApplePlatform.MacCatalyst:
return false;
default:
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
}
}
2016-04-21 15:57:02 +03:00
}
}