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);