diff --git a/Versions-mac.plist.in b/Versions-mac.plist.in index e4697ff9f663..02734a11e841 100644 --- a/Versions-mac.plist.in +++ b/Versions-mac.plist.in @@ -35,6 +35,7 @@ http-client-handlers mono-symbol-archive sgen-concurrent-gc + link-platform hybrid-aot diff --git a/docs/website/mmp-errors.md b/docs/website/mmp-errors.md index b03c50048588..12d05d87495b 100644 --- a/docs/website/mmp-errors.md +++ b/docs/website/mmp-errors.md @@ -146,6 +146,8 @@ The easiest way to get exact version information is to use the **Xamarin Studio*

MM202x: Binding Optimizer failed processing `...`.

+

MM2100: Xamarin.Mac Classic API does not support Platform Linking.

+ # MM3xxx: AOT ## MM30xx: AOT (general) errors diff --git a/docs/website/mtouch-errors.md b/docs/website/mtouch-errors.md index 51ef548da742..6f2d83f101d9 100644 --- a/docs/website/mtouch-errors.md +++ b/docs/website/mtouch-errors.md @@ -1142,6 +1142,7 @@ Something unexpected occured when trying to reduce the metadata from the applica Something unexpected occured when trying to mark `NSObject` subclasses from the application. The assembly causing the issue is named in the error message. In order to fix this issue the assembly will need to be provided in a [bug report](http://bugzilla.xamarin.com) along with a complete build log with verbosity enabled (i.e. `-v -v -v -v` in the **Additional mtouch arguments**). + # MT3xxx: AOT error messages diff --git a/msbuild/Xamarin.Mac.Tasks.Core/Tasks/MmpTaskBase.cs b/msbuild/Xamarin.Mac.Tasks.Core/Tasks/MmpTaskBase.cs index 1c294218c05a..c9d12bf35248 100644 --- a/msbuild/Xamarin.Mac.Tasks.Core/Tasks/MmpTaskBase.cs +++ b/msbuild/Xamarin.Mac.Tasks.Core/Tasks/MmpTaskBase.cs @@ -166,6 +166,9 @@ protected override string GenerateCommandLineCommands () case "sdkonly": args.Add ("/linksdkonly"); break; + case "platform": + args.Add ("/linkplatform"); + break; default: args.Add ("/nolink"); break; diff --git a/tests/common/mac/ClassicExample.csproj b/tests/common/mac/ClassicExample.csproj index 3d304c4b3ccc..a41160ce021b 100644 --- a/tests/common/mac/ClassicExample.csproj +++ b/tests/common/mac/ClassicExample.csproj @@ -21,7 +21,7 @@ false false false - false + %INCLUDE_MONO_RUNTIME% false Mac Developer false diff --git a/tests/common/mac/ProjectTestHelpers.cs b/tests/common/mac/ProjectTestHelpers.cs index 5aeaed387662..d90721bf9b03 100644 --- a/tests/common/mac/ProjectTestHelpers.cs +++ b/tests/common/mac/ProjectTestHelpers.cs @@ -248,10 +248,10 @@ public static OutputText TestUnifiedExecutable (UnifiedTestConfig config, bool s return new OutputText (buildOutput, runOutput); } - public static OutputText TestClassicExecutable (string tmpDir, string testCode = "", string csprojConfig = "", bool shouldFail = false) + public static OutputText TestClassicExecutable (string tmpDir, string testCode = "", string csprojConfig = "", bool shouldFail = false, bool includeMonoRuntime = false) { Guid guid = Guid.NewGuid (); - string csprojTarget = GenerateClassicEXEProject (tmpDir, "ClassicExample.csproj", testCode + GenerateOutputCommand (tmpDir,guid), csprojConfig, ""); + string csprojTarget = GenerateClassicEXEProject (tmpDir, "ClassicExample.csproj", testCode + GenerateOutputCommand (tmpDir,guid), csprojConfig, includeMonoRuntime: includeMonoRuntime); string buildOutput = BuildProject (csprojTarget, isUnified : false, diagnosticMSBuild: false, shouldFail : shouldFail); if (shouldFail) return new OutputText (buildOutput, ""); @@ -278,7 +278,7 @@ public static OutputText TestSystemMonoExecutable (UnifiedTestConfig config, boo return new OutputText (buildOutput, runOutput); } - public static string GenerateClassicEXEProject (string tmpDir, string projectName, string testCode, string csprojConfig = "", string references = "", string assemblyName = null) + public static string GenerateClassicEXEProject (string tmpDir, string projectName, string testCode, string csprojConfig = "", string references = "", string assemblyName = null, bool includeMonoRuntime = false) { WriteMainFile ("", testCode, false, false, Path.Combine (tmpDir, "Main.cs")); @@ -287,7 +287,7 @@ public static string GenerateClassicEXEProject (string tmpDir, string projectNam return CopyFileWithSubstitutions (Path.Combine (sourceDir, projectName), Path.Combine (tmpDir, projectName), text => { - return text.Replace ("%CODE%", csprojConfig).Replace ("%REFERENCES%", references).Replace ("%NAME%", assemblyName ?? Path.GetFileNameWithoutExtension (projectName)); + return text.Replace ("%CODE%", csprojConfig).Replace ("%REFERENCES%", references).Replace ("%NAME%", assemblyName ?? Path.GetFileNameWithoutExtension (projectName)).Replace ("%INCLUDE_MONO_RUNTIME%", includeMonoRuntime.ToString ()); }); } @@ -487,4 +487,4 @@ public static int RunCommand (string path, string args, string[] env = null, Str } } } -#endif \ No newline at end of file +#endif diff --git a/tests/mmptest/mmptest.csproj b/tests/mmptest/mmptest.csproj index 4784fd3cb229..2d248355a01e 100644 --- a/tests/mmptest/mmptest.csproj +++ b/tests/mmptest/mmptest.csproj @@ -70,6 +70,7 @@ unit\aot.cs + diff --git a/tests/mmptest/src/LinkerTests.cs b/tests/mmptest/src/LinkerTests.cs new file mode 100644 index 000000000000..6863de6a501c --- /dev/null +++ b/tests/mmptest/src/LinkerTests.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; +using System.Reflection; + +namespace Xamarin.MMP.Tests +{ + public partial class MMPTests + { + int GetNumberOfTypesInLibrary (string path) + { + string output = TI.RunAndAssert ("/Library/Frameworks/Mono.framework/Versions/Current/Commands/monop", new StringBuilder ("-r:" + path), "GetNumberOfTypesInLibrary"); + string[] splitBuildOutput = output.Split (new string[] { Environment.NewLine }, StringSplitOptions.None); + string outputLine = splitBuildOutput.First (x => x.StartsWith ("Total:")); + string numberSize = outputLine.Split (':')[1]; + string number = numberSize.Split (' ')[1]; + return int.Parse (number); + } + + string GetAppName (bool modern) => modern ? "UnifiedExample.app" : "XM45Example.app"; + string GetOutputBundlePath (string tmpDir, string name, bool modern) => Path.Combine (tmpDir, "bin/Debug/" + GetAppName (modern) + "/Contents/MonoBundle", name + ".dll"); + + string GetFrameworkName (bool modern) => modern ? "Xamarin.Mac" : "4.5"; + string GetBaseAssemblyPath (string name, bool modern) => Path.Combine (TI.FindRootDirectory (), "Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/" + GetFrameworkName (modern) + "/", name + ".dll"); + + const string PlatformProjectConfig = "Platform"; + + [Test] + public void ModernLinkingSDK_WithAllNonProductSkipped_BuildsWithSameNumberOfTypes () + { + RunMMPTest (tmpDir => { + string[] dependencies = { "mscorlib", "System.Core", "System" }; + string config = "SdkOnly--linkskip=" + dependencies.Aggregate ((arg1, arg2) => arg1 + " --linkskip=" + arg2) + ""; + TI.UnifiedTestConfig test = new TI.UnifiedTestConfig (tmpDir) { CSProjConfig = config }; + TI.TestUnifiedExecutable (test); + foreach (string dep in dependencies) { + int typesInBaseLib = GetNumberOfTypesInLibrary (GetBaseAssemblyPath (dep, true)); + int typesInOutput = GetNumberOfTypesInLibrary (GetOutputBundlePath (tmpDir, dep, true)); + Assert.AreEqual (typesInBaseLib, typesInOutput, $"We linked a linkskip - {dep} with config ({typesInBaseLib} vs {typesInOutput}:\n {config}"); + } + }); + } + + [Test] + public void FullLinkingSdk_BuildsWithFewerPlatformTypesOnly () + { + RunMMPTest (tmpDir => { + string[] nonPlatformDependencies = { "mscorlib", "System.Core", "System" }; + TI.UnifiedTestConfig test = new TI.UnifiedTestConfig (tmpDir) { CSProjConfig = PlatformProjectConfig, XM45 = true }; + TI.TestUnifiedExecutable (test); + foreach (string dep in nonPlatformDependencies) { + int typesInBaseLib = GetNumberOfTypesInLibrary (GetBaseAssemblyPath (dep, false)); + int typesInOutput = GetNumberOfTypesInLibrary (GetOutputBundlePath (tmpDir, dep, false)); + Assert.AreEqual (typesInBaseLib, typesInOutput, $"We linked a linkskip - {dep} with config ({typesInBaseLib} vs {typesInOutput}):\n {PlatformProjectConfig}"); + } + + int typesInBasePlatform = GetNumberOfTypesInLibrary (GetBaseAssemblyPath ("Xamarin.Mac", false)); + int typesInOutputPlatform = GetNumberOfTypesInLibrary (GetOutputBundlePath (tmpDir, "Xamarin.Mac", false)); + Assert.AreNotEqual (typesInBasePlatform, typesInOutputPlatform, $"We linked a linkskip - Xamarin.Mac with config ({typesInBasePlatform} vs {typesInOutputPlatform}):\n {PlatformProjectConfig}"); + + }); + } + + [Test] + public void PlatformSDKOnClassic_ShouldNotBeSupported () + { + RunMMPTest (tmpDir => { + TI.TestClassicExecutable (tmpDir, csprojConfig: "--linkplatform\n", includeMonoRuntime:true, shouldFail: true); + }); + } + } +} diff --git a/tools/common/Application.cs b/tools/common/Application.cs index 13e9bb5aeeff..27597e5da524 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -31,6 +31,9 @@ public enum LinkMode { None, SDKOnly, All, +#if !MONOTOUCH + Platform, +#endif } public partial class Application diff --git a/tools/linker/MobileMarkStep.cs b/tools/linker/MobileMarkStep.cs index f7b2dbe02b27..1f3a51e57713 100644 --- a/tools/linker/MobileMarkStep.cs +++ b/tools/linker/MobileMarkStep.cs @@ -549,8 +549,11 @@ void ProcessSystemXml (TypeDefinition type) switch (type.Name) { case "XslCompiledTransform": TypeDefinition nop = GetType ("System.Xml", "System.Xml.Xsl.NoOperationDebugger"); - MarkNamedMethod (nop, "OnCompile"); - MarkNamedMethod (nop, "OnExecute"); + // only available on the mobile profile + if (nop != null) { + MarkNamedMethod (nop, "OnCompile"); + MarkNamedMethod (nop, "OnExecute"); + } break; } break; diff --git a/tools/mmp/Tuning.cs b/tools/mmp/Tuning.cs index 5825f52db8ab..2016dd255ce1 100644 --- a/tools/mmp/Tuning.cs +++ b/tools/mmp/Tuning.cs @@ -73,14 +73,6 @@ class Linker { public static void Process (LinkerOptions options, out LinkContext context, out List assemblies) { - switch (options.TargetFramework.Identifier) { - case "Xamarin.Mac": - Profile.Current = new MacMobileProfile (options.Architecture == "x86_64" ? 64 : 32); - break; - default: - Profile.Current = new MonoMacProfile (); - break; - } Namespaces.Initialize (); var pipeline = CreatePipeline (options); @@ -89,6 +81,7 @@ public static void Process (LinkerOptions options, out LinkContext context, out context = CreateLinkContext (options, pipeline); context.Resolver.AddSearchDirectory (options.OutputDirectory); + context.KeepTypeForwarderOnlyAssemblies = (Profile.Current is XamarinMacProfile); try { pipeline.Process (context); @@ -163,15 +156,19 @@ static Pipeline CreatePipeline (LinkerOptions options) if (options.LinkMode != LinkMode.None) { pipeline.AppendStep (new TypeMapStep ()); - pipeline.AppendStep (new SubStepDispatcher { + var subdispatcher = new SubStepDispatcher { new ApplyPreserveAttribute (), - new CoreRemoveSecurity (), new OptimizeGeneratedCodeSubStep (options.EnsureUIThread), new RemoveUserResourcesSubStep (), new CoreRemoveAttributes (), new CoreHttpMessageHandler (options), new MarkNSObjects (), - }); + }; + // CoreRemoveSecurity can modify non-linked assemblies + // but the conditions for this cannot happen if only the platform assembly is linked + if (options.LinkMode != LinkMode.Platform) + subdispatcher.Add (new CoreRemoveSecurity ()); + pipeline.AppendStep (subdispatcher); pipeline.AppendStep (new MonoMacPreserveCode (options)); pipeline.AppendStep (new PreserveCrypto ()); @@ -251,12 +248,20 @@ protected override bool IsLinked (AssemblyDefinition assembly) if (link_mode == LinkMode.None) return false; + if (link_mode == LinkMode.Platform) + return Profile.IsProductAssembly (assembly); + return base.IsLinked (assembly); } protected override void ProcessAssembly (AssemblyDefinition assembly) { - if (link_mode == LinkMode.None) { + switch (link_mode) { + case LinkMode.Platform: + if (!Profile.IsProductAssembly (assembly)) + Annotations.SetAction (assembly, AssemblyAction.Copy); + break; + case LinkMode.None: Annotations.SetAction (assembly, AssemblyAction.Copy); return; } diff --git a/tools/mmp/driver.cs b/tools/mmp/driver.cs index 1d4f5c420456..1ef06e045580 100644 --- a/tools/mmp/driver.cs +++ b/tools/mmp/driver.cs @@ -274,6 +274,7 @@ static void Main2 (string [] args) } } }, + { "linkplatform", "Link only the Xamarin.Mac.dll platform assembly", v => App.LinkMode = LinkMode.Platform }, { "linksdkonly", "Link only the SDK assemblies", v => App.LinkMode = LinkMode.SDKOnly }, { "linkskip=", "Skip linking of the specified assembly", v => App.LinkSkipped.Add (v) }, { "i18n=", "List of i18n assemblies to copy to the output directory, separated by commas (none,all,cjk,mideast,other,rare,west)", v => App.I18n = LinkerOptions.ParseI18nAssemblies (v) }, @@ -453,6 +454,9 @@ static void Main2 (string [] args) if (targetFramework == TargetFramework.Empty) throw new MonoMacException (1404, true, "Target framework '{0}' is invalid.", userTargetFramework); + if (IsClassic && App.LinkMode == LinkMode.Platform) + throw new MonoMacException (2100, true, "Xamarin.Mac Classic API does not support Platofmr Linking."); + // sanity check as this should never happen: we start out by not setting any // Unified/Classic properties, and only IsUnifiedMobile if we are are on the // XM framework. If we are not, we set IsUnifiedFull to true iff we detect @@ -468,9 +472,16 @@ static void Main2 (string [] args) if (IsUnified == IsClassic || (IsUnified && IsUnifiedCount != 1)) throw new Exception ("IsClassic/IsUnified/IsUnifiedMobile/IsUnifiedFullSystemFramework/IsUnifiedFullXamMacFramework logic regression"); - if ((IsUnifiedFullSystemFramework || IsUnifiedFullXamMacFramework) && (App.LinkMode != LinkMode.None)) - throw new MonoMacException (2007, true, - "Xamarin.Mac Unified API against a full .NET framework does not support linking. Pass the -nolink flag."); + if (IsUnifiedFullSystemFramework || IsUnifiedFullXamMacFramework) { + switch (App.LinkMode) { + case LinkMode.None: + case LinkMode.Platform: + break; + default: + throw new MonoMacException (2007, true, + "Xamarin.Mac Unified API against a full .NET framework does not support linking SDK or All assemblies. Pass either the `-nolink` or `-linkplatform` flag."); + } + } ValidateXcode (); @@ -699,6 +710,13 @@ static void Pack (IList unprocessed) if (!File.Exists (root_assembly)) throw new MonoMacException (7, true, "The root assembly '{0}' does not exist", root_assembly); + if (IsClassic) + Profile.Current = new MonoMacProfile (); + else if (IsUnifiedFullXamMacFramework || IsUnifiedFullSystemFramework) + Profile.Current = new XamarinMacProfile (arch == "x86_64" ? 64 : 32); + else + Profile.Current = new MacMobileProfile (arch == "x86_64" ? 64 : 32); + string root_wo_ext = Path.GetFileNameWithoutExtension (root_assembly); if (Profile.IsSdkAssembly (root_wo_ext) || Profile.IsProductAssembly (root_wo_ext)) throw new MonoMacException (3, true, "Application name '{0}.exe' conflicts with an SDK or product assembly (.dll) name.", root_wo_ext);