diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs index cc1d5b6896ee..a0fce1fb423a 100644 --- a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs @@ -1,5 +1,7 @@ using Mono.Cecil; +#nullable disable + namespace Mono.Linker { public static class MethodDefinitionExtensions { public static bool IsDefaultConstructor (this MethodDefinition method) diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs index 6d2abeef6e04..c9d0b6b102f4 100644 --- a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs @@ -33,6 +33,8 @@ using Mono.Cecil; using Mono.Cecil.Cil; +#nullable disable + namespace Mono.Tuner { public static class MethodBodyRocks { diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs index 0f85e17196de..a5759b496ddd 100644 --- a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs @@ -5,6 +5,8 @@ using Mono.Linker; +#nullable disable + namespace Mono.Tuner { public static partial class Extensions { diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index bed4b2b282f3..49ab0125ece8 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -253,6 +253,7 @@ _ResolveAppExtensionReferences; _ExtendAppExtensionReferences; _ComputeLinkerArguments; + _PrepareAssemblies; _ComputeFrameworkFilesToPublish; _ComputeDynamicLibrariesToPublish; ComputeFilesToPublish; @@ -729,7 +730,7 @@ - <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.Steps.PreserveBlockCodeHandler" /> + <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true' And '$(PrepareAssemblies)' != 'true'" Type="Xamarin.Linker.Steps.PreserveBlockCodeHandler" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.OptimizeGeneratedCodeHandler" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.BackingFieldDelayHandler" /> <_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.MarkIProtocolHandler" /> @@ -1271,15 +1272,28 @@ - - - - + + + + + + + + + + + <_IntermediateAssemblyProperty>@(IntermediateAssembly) + <_PreparedIntermediateAssemblyProperty>@(PreparedIntermediateAssembly->WithMetadataValue('BeforePrepareAssembliesPath','$(_IntermediateAssemblyProperty)')) + + + <_PreparedRootedIntermediateAssembly Include="@(TrimmerRootAssembly->'$(_PreparedIntermediateAssemblyProperty)')" Condition="'%(Identity)' == '$(_IntermediateAssemblyProperty)'" /> + + + - + diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/PrepareAssemblies.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/PrepareAssemblies.cs new file mode 100644 index 000000000000..e93813f8459d --- /dev/null +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/PrepareAssemblies.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +using Xamarin.Build; +using Xamarin.Utils; + +#nullable enable + +namespace Xamarin.MacDev.Tasks { + public class PrepareAssemblies : XamarinTask { + const string ErrorPrefix = "AP"; + + #region Inputs + [Required] + public ITaskItem [] InputAssemblies { get; set; } = []; + + public string MakeReproPath { get; set; } = ""; + + public string OutputDirectory { get; set; } = ""; + #endregion + + #region Outputs + [Output] + public ITaskItem [] OutputAssemblies { get; set; } = []; + #endregion + + Dictionary map = new (); + + AssemblyPreparerInfo GetAssemblyInfo (ITaskItem item) + { + var inputPath = item.ItemSpec; + var outputPath = Path.Combine (OutputDirectory, Path.GetFileName (inputPath)); // FIXME: wrong for resource assemblies, at the very least. + var rv = new AssemblyPreparerInfo (inputPath, outputPath); + map [rv] = item; + return rv; + } + + public override bool Execute () + { + try { + var infos = InputAssemblies.Select (GetAssemblyInfo).ToArray (); + using var preparer = new AssemblyPreparer (infos, Platform); + preparer.MakeReproPath = MakeReproPath; + var rv = preparer.Prepare (out var exceptions); + + foreach (var pe in exceptions) { + if (pe.Error) { + Log.LogError (null, $"{ErrorPrefix}{pe.Code}", null, pe.FileName ?? "MSBuild", 0, 0, 0, 0, message: pe.Message); + Exception? ie = pe.InnerException; + while (ie is not null) { + Log.LogMessage (MessageImportance.Low, "Inner exception: {0}\n{1}", ie.Message, ie.StackTrace); + ie = ie.InnerException; + } + } else { + Log.LogWarning (null, $"{ErrorPrefix}{pe.Code}", null, pe.FileName ?? "MSBuild", 0, 0, 0, 0, message: pe.Message); + } + } + + OutputAssemblies = preparer.Assemblies.Select (v => { + var item = map [v]; + item.ItemSpec = v.OutputPath; + item.SetMetadata ("BeforePrepareAssembliesPath", v.InputPath); + return item; + }).ToArray (); + return rv && !Log.HasLoggedErrors; + } catch (Exception e) { + Log.LogError ("Unexpected error while preparing assemblies: {0}", e); + return false; + } + } + } +} diff --git a/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj b/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj index 6a2594989fc6..216fe9ab9d91 100644 --- a/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj +++ b/msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj @@ -38,6 +38,7 @@ + ProjectReference @@ -70,9 +71,6 @@ - - ApplePlatform.cs - StringUtils.cs diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets index 984ba1c916a5..6cb75c700807 100644 --- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets +++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets @@ -88,6 +88,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. + @@ -3259,6 +3260,48 @@ Copyright (C) 2018 Microsoft. All rights reserved. + + true + + + + + <_AssembliesToPrepare Include="@(ResolvedFileToPublish)" Condition="'%(ResolvedFileToPublish.PostprocessAssembly)' == 'true'" /> + + <__PDBToPrepare Include="@(ResolvedFileToPublish)" Exclude="@(_AssembliesToPrepare->'%(RelativeDir)%(Filename).pdb')" /> + <_PDBToPrepare Include="@(ResolvedFileToPublish)" Exclude="@(__PDBToLink)" /> + + + $([MSBuild]::EnsureTrailingSlash('$(DeviceSpecificIntermediateOutputPath)prepared-assemblies')) + + + + + + + + + + + + + + + diff --git a/src/ObjCRuntime/Constants.cs b/src/ObjCRuntime/Constants.cs index c1dd1d8476c3..0428bc6ed4a2 100644 --- a/src/ObjCRuntime/Constants.cs +++ b/src/ObjCRuntime/Constants.cs @@ -1,4 +1,4 @@ -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM namespace Xamarin.Bundler { #else namespace ObjCRuntime { diff --git a/src/ObjCRuntime/ErrorHelper.cs b/src/ObjCRuntime/ErrorHelper.cs index d3793878758b..9255beafa0b1 100644 --- a/src/ObjCRuntime/ErrorHelper.cs +++ b/src/ObjCRuntime/ErrorHelper.cs @@ -2,7 +2,7 @@ #nullable enable -#if MTOUCH || MMP || MMP_TEST || MTOUCH_TESTS +#if MTOUCH || MMP || MMP_TEST || MTOUCH_TESTS || ASSEMBLY_PREPARER #define BUNDLER #endif diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 9a2371fd9633..e4adb3998c0f 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -20,7 +20,7 @@ using ObjCRuntime; using Xamarin.Bundler; -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM using Xamarin.Utils; using TAssembly = Mono.Cecil.AssemblyDefinition; using TType = Mono.Cecil.TypeReference; @@ -93,7 +93,7 @@ public static List GetMT4127 (TMethod impl, List ifac } abstract partial class Registrar { -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM public Application App { get; protected set; } #endif @@ -156,7 +156,7 @@ internal class ObjCType { public bool IsCategory { get { return CategoryAttribute is not null; } } -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM HashSet all_protocols; // This contains all protocols in the type hierarchy. // Given a type T that implements a protocol with super protocols: @@ -884,7 +884,7 @@ public Trampoline Trampoline { if (trampoline != Trampoline.None) return trampoline; -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM throw ErrorHelper.CreateError (8018, Errors.MT8018); #else var mi = (System.Reflection.MethodInfo) Method; @@ -1331,7 +1331,7 @@ internal static string AppKit { } #endif -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM internal string AssemblyName { get { switch (App.Platform) { @@ -1379,7 +1379,7 @@ public string PlatformAssembly { } } -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM // "#if MTOUCH" code does not need locking when accessing 'types', because mtouch is single-threaded. public Dictionary Types { get { return types; } @@ -2205,7 +2205,7 @@ ObjCType RegisterTypeUnsafe (TType type, ref List exceptions) } } else { TMethod method = null; -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM method = attrib.Method; #endif var objcMethod = new ObjCMethod (this, objcType, method) { @@ -2261,7 +2261,7 @@ ObjCType RegisterTypeUnsafe (TType type, ref List exceptions) FieldType = "@", IsProperty = true, IsStatic = IsStatic (property), -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM Property = property, #endif }, ref exceptions); @@ -2657,7 +2657,7 @@ protected string GetExportedTypeName (TType type) string GetBoolEncoding () { // map managed 'bool' to ObjC BOOL = 'unsigned char' in OSX and 32bit iOS architectures and 'bool' in 64bit iOS architectures -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM switch (App.Platform) { case ApplePlatform.iOS: case ApplePlatform.TVOS: @@ -2782,7 +2782,7 @@ protected void UnlockRegistrar () System.Threading.Monitor.Exit (types); } -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM internal static void NSLog (string format, params object [] args) { Console.WriteLine (format, args); diff --git a/tests/assembly-preparer/GlobalUsings.cs b/tests/assembly-preparer/GlobalUsings.cs new file mode 100644 index 000000000000..68feec6faf4d --- /dev/null +++ b/tests/assembly-preparer/GlobalUsings.cs @@ -0,0 +1,9 @@ +global using System.IO; + +global using Mono.Cecil; +global using NUnit.Framework; + +global using Xamarin; +global using Xamarin.Build; +global using Xamarin.Tests; +global using Xamarin.Utils; diff --git a/tests/assembly-preparer/Makefile b/tests/assembly-preparer/Makefile new file mode 100644 index 000000000000..59fa8b078157 --- /dev/null +++ b/tests/assembly-preparer/Makefile @@ -0,0 +1,5 @@ +TOP=../.. +include $(TOP)/Make.config + +run-tests: + $(DOTNET) test *.csproj \ No newline at end of file diff --git a/tests/assembly-preparer/PreserveBlockCodeHandlerTests.cs b/tests/assembly-preparer/PreserveBlockCodeHandlerTests.cs new file mode 100644 index 000000000000..1313bddb548a --- /dev/null +++ b/tests/assembly-preparer/PreserveBlockCodeHandlerTests.cs @@ -0,0 +1,104 @@ +using Mono.Cecil.Rocks; + +namespace AssemblyPreparerTests; + +public class PreserveBlockCodeHandlerTests { + [Test] + [TestCase (ApplePlatform.MacCatalyst)] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacOSX)] + public void First (ApplePlatform platform) + { + var code = @" + using System; + using ObjCRuntime; + namespace ObjCRuntime; + class Trampolines { + static internal class SDInnerBlock { + // this field is not preserved by other means, but it must not be linked away + static internal readonly DInnerBlock Handler = Invoke; + + [MonoPInvokeCallback (typeof (DInnerBlock))] + static internal void Invoke (IntPtr block, int magic_number) + { + } + + public delegate void DInnerBlock (IntPtr block, int magic_number); + } + }"; + + var csproj = $@" + + + net$(BundledNETCoreAppTargetFrameworkVersion)-{platform.AsString ().ToLower ()} + false + true + + + "; + + var tmpdir = Cache.CreateTemporaryDirectory (); + File.WriteAllText (Path.Combine (tmpdir, "Test.cs"), code); + var csprojPath = Path.Combine (tmpdir, "Test.csproj"); + File.WriteAllText (csprojPath, csproj); + DotNet.AssertBuild (csprojPath); + var assemblyDir = Path.Combine (tmpdir, "bin", "Debug"); + + var assemblies = GetAssemblies (platform); + assemblies.Add (Path.Combine (assemblyDir, "Test.dll")); + var infos = assemblies.Select (v => new AssemblyPreparerInfo (v, Path.Combine (assemblyDir, "out", Path.GetFileName (v)))).ToArray (); + var preparer = new AssemblyPreparer (infos, platform); + Assert.That (preparer.Prepare (out var exceptions), Is.True, "Prepare"); + Assert.That (exceptions, Is.Empty, "Exceptions"); + + var outputPath = Path.Combine (assemblyDir, "out", "Test.dll"); + var assemblyDefinition = AssemblyDefinition.ReadAssembly (outputPath); + var type = assemblyDefinition.MainModule.Types.Single (v => v.Name == "Trampolines").NestedTypes.Single (v => v.Name == "SDInnerBlock"); + var cctor = type.GetStaticConstructor (); + var attribs = cctor.CustomAttributes?.OrderBy (v => string.Join (", ", v.ConstructorArguments.Select (v => v.Value?.ToString ()))).ToArray (); + Assert.That (attribs, Is.Not.Null, "Attributes"); + Assert.That (attribs.Count, Is.EqualTo (2), "Attribute count"); + Assert.That (attribs.All (v => v.AttributeType.Name == "DynamicDependencyAttribute"), Is.True, "Attribute name"); + Assert.That ((string) attribs [0].ConstructorArguments [0].Value, Is.EqualTo ("Handler"), "First attribute's first argument"); + Assert.That ((string) attribs [1].ConstructorArguments [0].Value, Is.EqualTo ("Invoke(System.IntPtr,System.Int32)"), "Second attribute's first argument"); + Assert.That (((TypeDefinition) attribs [0].ConstructorArguments [1].Value).FullName, Is.EqualTo ("ObjCRuntime.Trampolines/SDInnerBlock"), "First attribute's second argument"); + Assert.That (((TypeDefinition) attribs [1].ConstructorArguments [1].Value).FullName, Is.EqualTo ("ObjCRuntime.Trampolines/SDInnerBlock"), "Second attribute's second argument"); + } + + List GetAssemblies (ApplePlatform platform) + { + var assemblies = new List (); + string rid; + string packageName; + switch (platform) { + case ApplePlatform.MacCatalyst: + rid = "maccatalyst-arm64"; + packageName = "microsoft.netcore.app.runtime.mono.maccatalyst-arm64"; + break; + case ApplePlatform.iOS: + rid = "ios-arm64"; + packageName = "microsoft.netcore.app.runtime.mono.ios-arm64"; + break; + case ApplePlatform.TVOS: + rid = "tvos-arm64"; + packageName = "microsoft.netcore.app.runtime.mono.tvos-arm64"; + break; + case ApplePlatform.MacOSX: + rid = "osx-arm64"; + packageName = "microsoft.netcore.app.runtime.osx-arm64"; + break; + default: + throw new NotSupportedException ($"Unsupported platform: {platform}"); + } + var microsoftNetCoreAppRefPackageVersion = File.ReadAllLines (Path.Combine (Configuration.RootPath, "dotnet.config")).Single (v => v.StartsWith ("BUNDLED_NETCORE_PLATFORMS_PACKAGE_VERSION=", StringComparison.Ordinal)).Replace ("BUNDLED_NETCORE_PLATFORMS_PACKAGE_VERSION=", ""); + var bclDir = Path.Combine (Configuration.RootPath, "packages", packageName, microsoftNetCoreAppRefPackageVersion, "runtimes", rid, "lib", Configuration.DotNetTfm); + var nativeDir = Path.Combine (Configuration.RootPath, "packages", packageName, microsoftNetCoreAppRefPackageVersion, "runtimes", rid, "native"); + + assemblies.AddRange (Directory.GetFiles (bclDir, "*.dll")); + assemblies.AddRange (Directory.GetFiles (nativeDir, "*.dll")); + assemblies.Add (Path.Combine (Configuration.GetRuntimeDirectory (platform, rid), "lib", Configuration.DotNetTfm, Configuration.GetBaseLibraryName (platform))); + + return assemblies; + } +} diff --git a/tests/assembly-preparer/ReproTest.cs b/tests/assembly-preparer/ReproTest.cs new file mode 100644 index 000000000000..e7daa58523cd --- /dev/null +++ b/tests/assembly-preparer/ReproTest.cs @@ -0,0 +1,30 @@ +namespace AssemblyPreparerTests; + +public class ReproTest { + [Test] + public void Rerun () + { + var reproPath = Environment.GetEnvironmentVariable ("_PrepareAssembliesMakeReproPath"); + if (string.IsNullOrEmpty (reproPath)) + reproPath = "/tmp/assembly-preparer-repro"; + if (!Directory.Exists (reproPath)) + return; + + var platform = ApplePlatform.None; + var assemblies = new List (); + var lines = File.ReadAllLines (Path.Combine (reproPath, "arguments.txt")); + foreach (var line in lines) { + if (line.StartsWith ("Platform: ")) { + var platformStr = line.Substring ("Platform: ".Length); + platform = Enum.Parse (platformStr); + } else if (line.StartsWith ("Assembly: ")) { + var assembly = line.Substring ("Assembly: ".Length); + assemblies.Add (new AssemblyPreparerInfo (Path.Combine (reproPath, assembly), Path.Combine (reproPath, "out", assembly))); + } else { + throw new Exception ($"Unknown line: {line}"); + } + } + var ap = new AssemblyPreparer (assemblies.ToArray (), platform); + ap.Prepare (out var exceptions); + } +} diff --git a/tests/assembly-preparer/assembly-preparer-tests.csproj b/tests/assembly-preparer/assembly-preparer-tests.csproj new file mode 100644 index 000000000000..0a16e10a8edd --- /dev/null +++ b/tests/assembly-preparer/assembly-preparer-tests.csproj @@ -0,0 +1,53 @@ + + + net$(BundledNETCoreAppTargetFrameworkVersion) + latest + enable + enable + false + true + + + + + + + + + + + + + + external/tests/common/BinLog.cs + + + external/tests/mtouch/Cache.cs + + + external/tests/common/Configuration.cs + + + external/tests/common/ConfigurationNUnit.cs + + + external/tests/common/DotNet.cs + + + external/tools/common/Execution.cs + + + external/tests/common/ExecutionHelper.cs + + + external/tools/common/SdkVersions.cs + + + external/tools/common/StringUtils.cs + + + external/tools/common/TargetFramework.cs + + + + diff --git a/tests/assembly-preparer/assembly-preparer.sln b/tests/assembly-preparer/assembly-preparer.sln new file mode 100644 index 000000000000..65c7089325aa --- /dev/null +++ b/tests/assembly-preparer/assembly-preparer.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "assembly-preparer-tests", "assembly-preparer-tests.csproj", "{7FC61F95-BECF-E3B2-CFB2-185C4687F051}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "assembly-preparer", "..\..\tools\assembly-preparer\assembly-preparer.csproj", "{38C6599D-4B02-4F6B-A778-B468595C66BB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7FC61F95-BECF-E3B2-CFB2-185C4687F051}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FC61F95-BECF-E3B2-CFB2-185C4687F051}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FC61F95-BECF-E3B2-CFB2-185C4687F051}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FC61F95-BECF-E3B2-CFB2-185C4687F051}.Release|Any CPU.Build.0 = Release|Any CPU + {38C6599D-4B02-4F6B-A778-B468595C66BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38C6599D-4B02-4F6B-A778-B468595C66BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38C6599D-4B02-4F6B-A778-B468595C66BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38C6599D-4B02-4F6B-A778-B468595C66BB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6D0A58D0-09F3-4518-8D2E-577564445FB3} + EndGlobalSection +EndGlobal diff --git a/tests/common/DotNet.cs b/tests/common/DotNet.cs index f39ec36b451c..7cf367023ffc 100644 --- a/tests/common/DotNet.cs +++ b/tests/common/DotNet.cs @@ -37,7 +37,7 @@ public static ExecutionResult AssertPack (string project, Dictionary? properties = null, bool? msbuildParallelism = null) { var rv = Execute ("pack", project, properties, false, msbuildParallelism: msbuildParallelism); - Assert.AreNotEqual (0, rv.ExitCode, "Unexpected success"); + Assert.That (rv.ExitCode, Is.Not.EqualTo (0), "Unexpected success"); return rv; } @@ -49,7 +49,7 @@ public static ExecutionResult AssertPublish (string project, Dictionary? properties = null) { var rv = Execute ("publish", project, properties, false); - Assert.AreNotEqual (0, rv.ExitCode, "Unexpected success"); + Assert.That (rv.ExitCode, Is.Not.EqualTo (0), "Unexpected success"); return rv; } @@ -71,7 +71,7 @@ public static ExecutionResult AssertBuild (string project, Dictionary? properties = null) { var rv = Execute ("build", project, properties, false); - Assert.AreNotEqual (0, rv.ExitCode, "Unexpected success"); + Assert.That (rv.ExitCode, Is.Not.EqualTo (0), "Unexpected success"); return rv; } @@ -105,7 +105,7 @@ public static ExecutionResult AssertNew (string outputDirectory, string template if (rv.ExitCode != 0) { Console.WriteLine ($"'{Executable} {StringUtils.FormatArguments (args)}' failed with exit code {rv.ExitCode}."); Console.WriteLine (output); - Assert.AreEqual (0, rv.ExitCode, $"Exit code: {Executable} {StringUtils.FormatArguments (args)}"); + Assert.That (rv.ExitCode, Is.EqualTo (0), $"Exit code: {Executable} {StringUtils.FormatArguments (args)}"); } return new ExecutionResult (output, output, rv.ExitCode); } @@ -333,7 +333,7 @@ public static ExecutionResult Execute (string verb, string project, Dictionary exceptions) + { + exceptions = configuration.Exceptions; + + if (!string.IsNullOrEmpty (MakeReproPath)) { + if (File.Exists (MakeReproPath) || Directory.Exists (MakeReproPath)) { + configuration.Exceptions.Add (ErrorHelper.CreateError (99, $"Repro location already exists: {MakeReproPath}")); + return false; + } + Directory.CreateDirectory (MakeReproPath); + var lines = new List (); + lines.Add ($"Platform: {configuration.Platform}"); + foreach (var assembly in Assemblies) { + lines.Add ($"Assembly: {Path.GetFileName (assembly.InputPath)}"); + File.Copy (assembly.InputPath, Path.Combine (MakeReproPath, Path.GetFileName (assembly.InputPath))); + } + File.WriteAllLines (Path.Combine (MakeReproPath, "arguments.txt"), lines); + log.Log ($"Created repro in {MakeReproPath}"); + } + + var markHandlers = new IMarkHandler [] { + new PreserveBlockCodeHandler (), + }; + + var linkContext = new DerivedLinkContext (configuration); + configuration.DerivedLinkContext = linkContext; + + var markContext = new MarkContext (); + foreach (var handler in markHandlers) { + handler.Initialize (linkContext, markContext); + } + + // load assemblies + + var assemblyResolver = new DefaultAssemblyResolver (); + // var metadataResolver = new DefaultMetadataResolver (); + + var parameters = new ReaderParameters { + AssemblyResolver = assemblyResolver, + // MetadataResolver = metadataResolver, + ReadSymbols = true, + SymbolReaderProvider = new DefaultSymbolReaderProvider (throwIfNoSymbol: false), + }; + foreach (var assembly in Assemblies) { + var assemblyDefinition = AssemblyDefinition.ReadAssembly (assembly.InputPath, parameters); // FIXME: symbols + linkContext.Assemblies.Add (assemblyDefinition); + assembly.Assembly = assemblyDefinition; + configuration.Context.Annotations.SetAction (assemblyDefinition, AssemblyAction.Copy); + } + + foreach (var assembly in linkContext.GetAssemblies ()) { + // Skip SDK asemblies, they have nothing we need to process at the moment. + if (!assembly.MainModule.HasAssemblyReferences) + continue; + if (!configuration.IsProductAssembly (assembly) && !assembly.MainModule.AssemblyReferences.Any (v => configuration.IsProductAssembly (v.Name))) + continue; + + foreach (var type in assembly.MainModule.Types) { + markContext.MarkType (type); + } + } + + // save assemblies + + foreach (var assembly in Assemblies) { + var assemblyDefinition = assembly.Assembly!; + + var action = configuration.Context.Annotations.GetAction (assemblyDefinition); + switch (action) { + case AssemblyAction.Copy: + assembly.OutputPath = assembly.InputPath; + continue; + case AssemblyAction.Link: + case AssemblyAction.Save: + Console.WriteLine ($"Saving {assembly.InputPath} to {assembly.OutputPath}"); + break; + default: + throw new NotImplementedException ($"Unknown link action: {action}"); + } + + Directory.CreateDirectory (Path.GetDirectoryName (assembly.OutputPath)!); + var writerParameters = new WriterParameters (); + if (assemblyDefinition.MainModule.HasSymbols) { + var provider = new CustomSymbolWriterProvider (); + try { + using (var tmp = provider.GetSymbolWriter (assemblyDefinition.MainModule, Path.ChangeExtension (assembly.OutputPath, ".pdb"))) { } + File.Delete (Path.ChangeExtension (assembly.OutputPath, ".pdb")); + writerParameters.WriteSymbols = true; + writerParameters.SymbolWriterProvider = provider; + } catch (Exception e) { + Console.WriteLine ($"Failed to create symbol writer for {assembly.OutputPath}, not writing symbols: {e.Message}"); + } + } + try { + assemblyDefinition.Write (assembly.OutputPath, writerParameters); + } catch (Exception e) { + Console.WriteLine ($"Failed to write {assembly.OutputPath}: {e}"); + throw; + } + } + + return true; + } + + public void Dispose () + { + foreach (var assembly in Assemblies) + assembly.Assembly?.Dispose (); + } +} + +public class AssemblyPreparerInfo { + internal AssemblyDefinition? Assembly { get; set; } + + public string InputPath { get; private set; } + public string OutputPath { get; set; } + + public AssemblyPreparerInfo (string inputPath, string outputPath) + { + InputPath = inputPath; + OutputPath = outputPath; + } +} diff --git a/tools/assembly-preparer/GlobalUsings.cs b/tools/assembly-preparer/GlobalUsings.cs new file mode 100644 index 000000000000..c429b6757790 --- /dev/null +++ b/tools/assembly-preparer/GlobalUsings.cs @@ -0,0 +1,7 @@ +global using System; +global using System.Collections.Generic; +global using System.Linq; +global using Xamarin.Linker; + +namespace Xamarin.Tuner { } +namespace Mono.Linker.Steps { } diff --git a/tools/assembly-preparer/IAssemblyPreparerLog.cs b/tools/assembly-preparer/IAssemblyPreparerLog.cs new file mode 100644 index 000000000000..fbc16aad17aa --- /dev/null +++ b/tools/assembly-preparer/IAssemblyPreparerLog.cs @@ -0,0 +1,30 @@ +using System; +using Xamarin.Bundler; + +namespace Xamarin.Build; + +public interface IAssemblyPreparerLog { + void Log (string message); +} + +public class ConsoleLog : IAssemblyPreparerLog { + public void Log (string message) + { + Console.WriteLine (message); + } +} + +class AggregateLog : IAssemblyPreparerLog { + List logs = new (); + + public void Add (IAssemblyPreparerLog log) + { + logs.Add (log); + } + + public void Log (string message) + { + foreach (var log in logs) + log.Log (message); + } +} diff --git a/tools/assembly-preparer/Makefile b/tools/assembly-preparer/Makefile new file mode 100644 index 000000000000..bdfabb2fc468 --- /dev/null +++ b/tools/assembly-preparer/Makefile @@ -0,0 +1,6 @@ +TOP=../.. +include $(TOP)/Make.config +include $(TOP)/mk/rules.mk + +all-local:: + $(DOTNET) build /bl *.csproj diff --git a/tools/assembly-preparer/Scaffolding/AnnotationStore.cs b/tools/assembly-preparer/Scaffolding/AnnotationStore.cs new file mode 100644 index 000000000000..3359b92933e3 --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/AnnotationStore.cs @@ -0,0 +1,26 @@ +using Mono.Cecil; + +namespace Mono.Linker; + +public class AnnotationStore { + Dictionary assemblyActions = new Dictionary (); + public AssemblyAction GetAction (AssemblyDefinition assembly) + { + if (assemblyActions.TryGetValue (assembly, out var action)) + return action; + throw new InvalidOperationException ($"Assembly {assembly.Name} not found in the annotation store"); + } + public void SetAction (AssemblyDefinition assembly, AssemblyAction action) + { + assemblyActions [assembly] = action; + } + + public void Mark (TypeDefinition type) + { + throw new NotImplementedException (); + } + public void Mark (ExportedType type) + { + throw new NotImplementedException (); + } +} diff --git a/tools/assembly-preparer/Scaffolding/Application.cs b/tools/assembly-preparer/Scaffolding/Application.cs new file mode 100644 index 000000000000..062043b37835 --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/Application.cs @@ -0,0 +1,12 @@ +using Xamarin.Utils; + +namespace Xamarin.Bundler; + +public class Application { + public ApplePlatform Platform { get => throw new NotImplementedException (); } + public string ProductName => "assembly-preparer"; + public void LoadSymbols () + { + throw new NotImplementedException (); + } +} diff --git a/tools/assembly-preparer/Scaffolding/AssemblyAction.cs b/tools/assembly-preparer/Scaffolding/AssemblyAction.cs new file mode 100644 index 000000000000..364ac0c74697 --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/AssemblyAction.cs @@ -0,0 +1,7 @@ +namespace Mono.Linker; + +public enum AssemblyAction { + Copy, + Link, + Save, +} diff --git a/tools/assembly-preparer/Scaffolding/DerivedLinkContext.cs b/tools/assembly-preparer/Scaffolding/DerivedLinkContext.cs new file mode 100644 index 000000000000..74172ea37bc2 --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/DerivedLinkContext.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Tuner; + +using Mono.Linker; +using Xamarin.Bundler; + +public class DerivedLinkContext : LinkContext { + public DerivedLinkContext (LinkerConfiguration configuration) : base (configuration) + { + } +} diff --git a/tools/assembly-preparer/Scaffolding/Driver.cs b/tools/assembly-preparer/Scaffolding/Driver.cs new file mode 100644 index 000000000000..95185356f591 --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/Driver.cs @@ -0,0 +1,5 @@ +namespace Xamarin.Bundler; + +public static class Driver { + public const string CorlibName = "System.Private.CoreLib"; +} diff --git a/tools/assembly-preparer/Scaffolding/IMarkHandler.cs b/tools/assembly-preparer/Scaffolding/IMarkHandler.cs new file mode 100644 index 000000000000..dc9286ad5610 --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/IMarkHandler.cs @@ -0,0 +1,16 @@ +namespace Mono.Linker; + +using Mono.Cecil; + +public interface IMarkHandler { + void Initialize (LinkContext context, MarkContext markContext); + // void Initialize (LinkContext context, MarkContext markContext); + // void ProcessAssembly (AssemblyDefinition assembly); + // void ProcessType (TypeDefinition type); + // void ProcessField (FieldDefinition field); + // void ProcessMethod (MethodDefinition method); + // void ProcessParameter (ParameterDefinition parameter); + // void ProcessProperty (PropertyDefinition property); + // void ProcessEvent (EventDefinition eventDef); + +} diff --git a/tools/assembly-preparer/Scaffolding/LinkContext.cs b/tools/assembly-preparer/Scaffolding/LinkContext.cs new file mode 100644 index 000000000000..7a882b608a22 --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/LinkContext.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Mono.Cecil; +using Xamarin.Bundler; + +namespace Mono.Linker; + +public class LinkContext { + AnnotationStore annotations = new AnnotationStore (); + public AnnotationStore Annotations { get => annotations; } + + public List Assemblies = new List (); + public AssemblyDefinition [] GetAssemblies () { return Assemblies.ToArray (); } + + public LinkerConfiguration Configuration { get; private set; } + + public LinkContext (LinkerConfiguration configuration) + { + Configuration = configuration; + } +} diff --git a/tools/assembly-preparer/Scaffolding/LinkerConfiguration.cs b/tools/assembly-preparer/Scaffolding/LinkerConfiguration.cs new file mode 100644 index 000000000000..7ee1f7087f6d --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/LinkerConfiguration.cs @@ -0,0 +1,76 @@ +using System; +using Mono.Cecil; +using Xamarin.Linker; +using Xamarin.Tuner; +using Mono.Linker; +using System.Collections.Generic; +using Xamarin.Utils; + +namespace Xamarin.Bundler; + +public class LinkerConfiguration { + List exceptions = new List (); + public List Exceptions { + get { + return exceptions; + } + } + + AppBundleRewriter? abr; + internal AppBundleRewriter AppBundleRewriter { + get { + if (abr is null) + abr = new AppBundleRewriter (this); + return abr; + } + } + + DerivedLinkContext? derivedLinkContext; + public DerivedLinkContext DerivedLinkContext { + get { + if (derivedLinkContext is null) + throw new InvalidOperationException ($"No derived link context set?"); + return derivedLinkContext!; + } + set => derivedLinkContext = value; + } + public DerivedLinkContext Context { get => DerivedLinkContext; } + public Profile Profile { get => throw new NotImplementedException (); } + public AssemblyDefinition [] Assemblies { get => Context.GetAssemblies (); } + + public ApplePlatform Platform { get; set; } + public string PlatformAssembly { + get { + return $"Microsoft.{Platform.AsString ()}"; + } + } + public static LinkerConfiguration GetInstance (LinkContext context) + { + return context.Configuration; + } + public static void Report (LinkContext context, Exception exception) + { + var pe = exception as ProductException; + if (pe is null) + pe = ErrorHelper.CreateError (99, exception, "Unexpected error: {0}}", exception.Message); + GetInstance (context).exceptions.Add (pe); + } + + public static void Report (LinkContext context, List exceptions) + { + foreach (var exception in exceptions) { + Report (context, exception); + } + } + + public bool IsProductAssembly (string assemblyName) + { + return assemblyName == PlatformAssembly; + } + + public bool IsProductAssembly (AssemblyDefinition assembly) + { + return assembly.Name.Name == PlatformAssembly; + } + +} diff --git a/tools/assembly-preparer/Scaffolding/MarkContext.cs b/tools/assembly-preparer/Scaffolding/MarkContext.cs new file mode 100644 index 000000000000..acfa37cb746e --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/MarkContext.cs @@ -0,0 +1,37 @@ +namespace Mono.Linker; + +using System.Collections.Generic; +using Mono.Cecil; + +public class MarkContext { + List> markTypeActions = new List> (); + + // takes care of nested types as well + public void MarkType (TypeDefinition type) + { + foreach (var action in markTypeActions) { + action (type); + } + + if (type.HasNestedTypes) { + foreach (var nested in type.NestedTypes) { + MarkType (nested); + } + } + } + + public void RegisterMarkAssemblyAction (Action action) + { + throw new NotImplementedException (); + } + + public void RegisterMarkTypeAction (Action action) + { + markTypeActions.Add (action); + } + + public void RegisterMarkMethodAction (Action action) + { + throw new NotImplementedException (); + } +} diff --git a/tools/assembly-preparer/Scaffolding/Profile.cs b/tools/assembly-preparer/Scaffolding/Profile.cs new file mode 100644 index 000000000000..bc642085fdeb --- /dev/null +++ b/tools/assembly-preparer/Scaffolding/Profile.cs @@ -0,0 +1,5 @@ +namespace Mono.Linker; + +public class Profile { + +} diff --git a/tools/assembly-preparer/assembly-preparer.csproj b/tools/assembly-preparer/assembly-preparer.csproj new file mode 100644 index 000000000000..d12f7a6924e5 --- /dev/null +++ b/tools/assembly-preparer/assembly-preparer.csproj @@ -0,0 +1,132 @@ + + + + assembly-preparer + net$(BundledNETCoreAppTargetFrameworkVersion);netstandard2.0 + $(DefineConstants);PRETRIM;ASSEMBLY_PREPARER + Library + true + latest + enable + + + + + + + + + + + external/tools/common/ApplePlatform.cs + + + external/tools/common/error.cs + + + external/tools/common/ErrorHelper.tools.cs + + + external/tools/dotnet-linker/AppBundleRewriter.cs + + + external/tools/dotnet-linker/Steps/ConfigurationAwareMarkHandler.cs + + + external/tools/dotnet-linker/Steps/ExceptionalMarkHandler.cs + + + external/tools/dotnet-linker/Steps/PreserveBlockCodeHandler.cs + + + external/tools/linker/CustomSymbolWriter.cs + + + external/src/ObjCRuntime/ErrorHelper.cs + + + external/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs + + + external/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs + + + external/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs + + + external/tools/dotnet-linker/CecilExtensions.cs + + + external/tools/dotnet-linker/DocumentionComments.cs + + + external/tools/common/SdkVersions.cs + + + + + + external/tools/mtouch/Errors.designer.cs + Errors.resx + + + external/tools/mtouch/Errors.resx + ResXFileCodeGenerator + Errors.designer.cs + Xamarin.Bundler + Xamarin.Bundler.Errors + + + external/tools/mtouch/Errors.cs.resx + Xamarin.Bundler.Errors.cs + + + external/tools/mtouch/Errors.de.resx + Xamarin.Bundler.Errors.de + + + external/tools/mtouch/Errors.es.resx + Xamarin.Bundler.Errors.es + + + external/tools/mtouch/Errors.fr.resx + Xamarin.Bundler.Errors.fr + + + external/tools/mtouch/Errors.it.resx + Xamarin.Bundler.Errors.it + + + external/tools/mtouch/Errors.ja.resx + Xamarin.Bundler.Errors.ja + + + external/tools/mtouch/Errors.ko.resx + Xamarin.Bundler.Errors.ko + + + external/tools/mtouch/Errors.pl.resx + Xamarin.Bundler.Errors.pl + + + external/tools/mtouch/Errors.pt-BR.resx + Xamarin.Bundler.Errors.pt-BR + + + external/tools/mtouch/Errors.ru.resx + Xamarin.Bundler.Errors.ru + + + external/tools/mtouch/Errors.tr.resx + Xamarin.Bundler.Errors.tr + + + external/tools/mtouch/Errors.zh-Hans.resx + Xamarin.Bundler.Errors.zh-Hans + + + external/tools/mtouch/Errors.zh-Hant.resx + Xamarin.Bundler.Errors.zh-Hant + + + diff --git a/tools/assembly-preparer/assembly-preparer.sln b/tools/assembly-preparer/assembly-preparer.sln new file mode 100644 index 000000000000..459364162482 --- /dev/null +++ b/tools/assembly-preparer/assembly-preparer.sln @@ -0,0 +1,50 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "assembly-preparer", "assembly-preparer.csproj", "{93089210-F742-462A-95CA-33CFF3B4D906}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "assembly-preparer-tests", "..\..\tests\assembly-preparer\assembly-preparer-tests.csproj", "{D5C190C5-2A98-47B4-BC51-9135F1B4D18E}" +EndProject +Global + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {93089210-F742-462A-95CA-33CFF3B4D906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Debug|x64.ActiveCfg = Debug|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Debug|x64.Build.0 = Debug|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Debug|x86.ActiveCfg = Debug|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Debug|x86.Build.0 = Debug|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Release|Any CPU.Build.0 = Release|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Release|x64.ActiveCfg = Release|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Release|x64.Build.0 = Release|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Release|x86.ActiveCfg = Release|Any CPU + {93089210-F742-462A-95CA-33CFF3B4D906}.Release|x86.Build.0 = Release|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Debug|x64.Build.0 = Debug|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Debug|x86.ActiveCfg = Debug|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Debug|x86.Build.0 = Debug|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Release|Any CPU.Build.0 = Release|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Release|x64.ActiveCfg = Release|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Release|x64.Build.0 = Release|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Release|x86.ActiveCfg = Release|Any CPU + {D5C190C5-2A98-47B4-BC51-9135F1B4D18E}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + EndGlobalSection +EndGlobal diff --git a/tools/common/Application.cs b/tools/common/Application.cs index f8901565d8c9..b40e9a208891 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -28,6 +28,7 @@ using PlatformResolver = Xamarin.Bundler.MonoMacResolver; #elif NET && !LEGACY_TOOLS using PlatformResolver = Xamarin.Linker.DotNetResolver; +#elif PRETRIM #else #error Invalid defines #endif diff --git a/tools/common/Frameworks.cs b/tools/common/Frameworks.cs index 7fb52455ad71..d649063e6bc9 100644 --- a/tools/common/Frameworks.cs +++ b/tools/common/Frameworks.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM using Mono.Cecil; using Xamarin.Bundler; @@ -32,7 +32,7 @@ public string LibraryPath { } } -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM public bool IsFrameworkAvailableInSimulator (Application app) { if (VersionAvailableInSimulator is null) @@ -688,7 +688,7 @@ public static Frameworks GetFrameworks (ApplePlatform platform, bool is_simulato } } -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM static void Gather (Application app, AssemblyDefinition product_assembly, HashSet frameworks, HashSet weak_frameworks, Func include_framework) { var namespaces = new HashSet (); diff --git a/tools/common/Make.common b/tools/common/Make.common index 248e8f480575..910fe4cf662d 100644 --- a/tools/common/Make.common +++ b/tools/common/Make.common @@ -1,5 +1,6 @@ # We check in SdkVersions.cs so that it's easier to use this file when building tests on Windows. +all-local:: $(abspath ../common/SdkVersions.cs) $(abspath ../common/SdkVersions.cs): ../common/SdkVersions.in.cs Makefile $(TOP)/Make.config $(TOP)/Make.versions $(Q_GEN) sed \ -e 's/@IOS_SDK_VERSION@/$(IOS_SDK_VERSION)/g' -e 's/@TVOS_SDK_VERSION@/$(TVOS_SDK_VERSION)/' -e 's/@MACOS_SDK_VERSION@/$(MACOS_SDK_VERSION)/' \ @@ -46,6 +47,7 @@ $(abspath ../common/SdkVersions.cs): ../common/SdkVersions.in.cs Makefile $(TOP) $(Q) if ! diff $@ $@.tmp >/dev/null; then $(CP) $@.tmp $@; git diff "$@"; echo "The file $(TOP)/tools/common/SdkVersions.cs has been automatically re-generated; please commit the changes."; exit 1; fi $(Q) touch $@ +all-local:: $(abspath ../common/ProductConstants.cs) $(abspath ../common/ProductConstants.cs): ../common/ProductConstants.in.cs Makefile $(TOP)/Make.config $(GIT_DIRECTORY)/index $(Q_GEN) sed \ $(foreach platform,$(DOTNET_PLATFORMS_UPPERCASE),-e 's/@$(platform)_REVISION@/$($(platform)_COMMIT_DISTANCE) ($(CURRENT_BRANCH_SED_ESCAPED): $(CURRENT_HASH))/g') \ @@ -54,3 +56,4 @@ $(abspath ../common/ProductConstants.cs): ../common/ProductConstants.in.cs Makef \ -e "s/@PRODUCT_HASH@/$(CURRENT_HASH_LONG)/g" \ $< > $@ + diff --git a/tools/common/Makefile b/tools/common/Makefile new file mode 100644 index 000000000000..6a27eba55f77 --- /dev/null +++ b/tools/common/Makefile @@ -0,0 +1,5 @@ +TOP=../.. + +include $(TOP)/Make.config +include $(TOP)/mk/rules.mk +include ../common/Make.common diff --git a/tools/common/ProductConstants.in.cs b/tools/common/ProductConstants.in.cs index 8272feab2785..5244ce633b68 100644 --- a/tools/common/ProductConstants.in.cs +++ b/tools/common/ProductConstants.in.cs @@ -1,6 +1,6 @@ using System; -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM using Xamarin.Bundler; using Xamarin.Utils; #endif diff --git a/tools/common/SdkVersions.cs b/tools/common/SdkVersions.cs index 7b6570cf5a81..8fb6385e9799 100644 --- a/tools/common/SdkVersions.cs +++ b/tools/common/SdkVersions.cs @@ -1,6 +1,6 @@ using System; -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM using Xamarin.Bundler; #endif @@ -69,7 +69,7 @@ static class SdkVersions { public static Version XcodeVersion { get { return new Version (Xcode); } } -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM public static Version GetVersion (Application app) { switch (app.Platform) { diff --git a/tools/common/SdkVersions.in.cs b/tools/common/SdkVersions.in.cs index 560c806fa57c..5c2bdd405bcd 100644 --- a/tools/common/SdkVersions.in.cs +++ b/tools/common/SdkVersions.in.cs @@ -1,6 +1,6 @@ using System; -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM using Xamarin.Bundler; #endif @@ -69,7 +69,7 @@ static class SdkVersions { public static Version XcodeVersion { get { return new Version (Xcode); } } -#if MTOUCH || MMP || BUNDLER +#if MTOUCH || MMP || BUNDLER || PRETRIM public static Version GetVersion (Application app) { switch (app.Platform) { diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 37f0ea5ee52d..bd35cf230423 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -25,6 +25,7 @@ using PlatformResolver = Xamarin.Bundler.MonoMacResolver; #elif NET using PlatformResolver = Xamarin.Linker.DotNetResolver; +#elif PRETRIM #else #error Invalid defines #endif diff --git a/tools/common/Target.cs b/tools/common/Target.cs index def2a09e6bf1..03c48f7c7697 100644 --- a/tools/common/Target.cs +++ b/tools/common/Target.cs @@ -34,6 +34,7 @@ using LinkerOptions = Xamarin.Linker.LinkerConfiguration; using PlatformLinkContext = Xamarin.Tuner.DerivedLinkContext; using PlatformResolver = Xamarin.Linker.DotNetResolver; +#elif PRETRIM #else #error Invalid defines #endif diff --git a/tools/common/cache.cs b/tools/common/cache.cs index e11c0a1b19c4..b866eab99582 100644 --- a/tools/common/cache.cs +++ b/tools/common/cache.cs @@ -15,6 +15,8 @@ public class Cache { const string NAME = "mtouch"; #elif BUNDLER const string NAME = "dotnet-linker"; +#elif PRETRIM + const string NAME = "assembly-preparer"; #else #error Wrong defines #endif diff --git a/tools/dotnet-linker/AppBundleRewriter.cs b/tools/dotnet-linker/AppBundleRewriter.cs index 05d1805a7a37..7e1f48a8d854 100644 --- a/tools/dotnet-linker/AppBundleRewriter.cs +++ b/tools/dotnet-linker/AppBundleRewriter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; - +using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Linker; @@ -10,6 +10,7 @@ using Xamarin.Bundler; using Xamarin.Linker; +using Xamarin.Utils; #nullable enable @@ -61,6 +62,8 @@ public AppBundleRewriter (LinkerConfiguration configuration) // Find corlib and the platform assemblies foreach (var asm in configuration.Assemblies) { if (asm.Name.Name == Driver.CorlibName) { + if (corlib_assembly is not null) + throw new InvalidOperationException ($"Already have a corlib assembly named {corlib_assembly.Name}"); corlib_assembly = asm; } else if (asm.Name.Name == configuration.PlatformAssembly) { platform_assembly = asm; @@ -1268,7 +1271,11 @@ public CustomAttribute CreateDynamicDependencyAttribute (string memberSignature, return attribute; } +#if NET public CustomAttribute CreateDynamicDependencyAttribute (DynamicallyAccessedMemberTypes memberTypes, TypeDefinition type) +#else + public CustomAttribute CreateDynamicDependencyAttribute (int memberTypes, TypeDefinition type) +#endif { var attribute = new CustomAttribute (DynamicDependencyAttribute_ctor__DynamicallyAccessedMemberTypes_Type); // typed as 'int' because that's how the linker expects it: https://github.com/dotnet/runtime/blob/3c5ad6c677b4a3d12bc6a776d654558cca2c36a9/src/tools/illink/src/linker/Linker/DynamicDependency.cs#L97 @@ -1276,5 +1283,39 @@ public CustomAttribute CreateDynamicDependencyAttribute (DynamicallyAccessedMemb attribute.ConstructorArguments.Add (new CustomAttributeArgument (System_Type, type)); return attribute; } + + + public void AddDynamicDependencyAttributeToStaticConstructor (TypeDefinition onType, MethodDefinition forMethod) + { + AddDynamicDependencyAttributeToStaticConstructor (onType, DocumentationComments.GetSignature (forMethod)); + } + + public void AddDynamicDependencyAttributeToStaticConstructor (TypeDefinition onType, FieldDefinition forField) + { + AddDynamicDependencyAttributeToStaticConstructor (onType, DocumentationComments.GetSignature (forField)); + } + + void AddDynamicDependencyAttributeToStaticConstructor (TypeDefinition onType, string signature) + { + ClearCurrentAssembly (); + SetCurrentAssembly (onType.Module.Assembly); + + var cctor = GetOrCreateStaticConstructor (onType); + var attrib = CreateDynamicDependencyAttribute (signature, onType); + cctor.CustomAttributes.Add (attrib); + + ClearCurrentAssembly (); + } + MethodDefinition GetOrCreateStaticConstructor (TypeDefinition type) + { + var staticCtor = type.GetTypeConstructor (); + if (staticCtor is null) { + staticCtor = type.AddMethod (".cctor", Mono.Cecil.MethodAttributes.Private | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.RTSpecialName | Mono.Cecil.MethodAttributes.SpecialName | Mono.Cecil.MethodAttributes.Static, System_Void); + staticCtor.CreateBody (out var il); + il.Emit (OpCodes.Ret); + } + + return staticCtor; + } } } diff --git a/tools/dotnet-linker/Steps/PreserveBlockCodeHandler.cs b/tools/dotnet-linker/Steps/PreserveBlockCodeHandler.cs index 63dfee5b5742..9aaebe19ead9 100644 --- a/tools/dotnet-linker/Steps/PreserveBlockCodeHandler.cs +++ b/tools/dotnet-linker/Steps/PreserveBlockCodeHandler.cs @@ -2,7 +2,8 @@ using System.Linq; using Mono.Cecil; - +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; using Mono.Linker; using Mono.Linker.Steps; using Mono.Tuner; @@ -18,6 +19,7 @@ public class PreserveBlockCodeHandler : ConfigurationAwareMarkHandler { protected override string Name { get; } = "Preserve Block Code"; protected override int ErrorCode { get; } = 2240; + AppBundleRewriter abr { get { return Configuration.AppBundleRewriter; } } public override void Initialize (LinkContext context, MarkContext markContext) { base.Initialize (context); @@ -79,9 +81,16 @@ and the Invoke method. if (method is null) return; - // The type was used, so preserve the method and field - Context.Annotations.Mark (method); - Context.Annotations.Mark (field); + // Preserve the method and field on the static constructor of the type. + abr.AddDynamicDependencyAttributeToStaticConstructor (type, method); + abr.AddDynamicDependencyAttributeToStaticConstructor (type, field); + // Remove the BeforeFieldInit attribute from the type, otherwise the linker may trim away the static constructor, and taking our DynamicDependency attributes with it. + type.Attributes &= ~TypeAttributes.BeforeFieldInit; + +#if ASSEMBLY_PREPARER + abr.SetCurrentAssembly (type.Module.Assembly); + abr.SaveCurrentAssembly (); +#endif } } } diff --git a/tools/linker/CustomSymbolWriter.cs b/tools/linker/CustomSymbolWriter.cs index eadad6cfaebf..db4f79b22316 100644 --- a/tools/linker/CustomSymbolWriter.cs +++ b/tools/linker/CustomSymbolWriter.cs @@ -78,7 +78,7 @@ static ImageDebugHeaderEntry ProcessEntry (ImageDebugHeaderEntry entry) public void Write (MethodDebugInformation info) => _symbolWriter.Write (info); public void Write () => _symbolWriter.Write (); public void Dispose () => _symbolWriter.Dispose (); -#if NET && !LEGACY_TOOLS +#if (NET && !LEGACY_TOOLS) || ASSEMBLY_PREPARER public void Write (ICustomDebugInformationProvider provider) => _symbolWriter.Write (provider); #endif }