diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index b8a19b2cc698..5c76b98d6cd8 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -493,6 +493,7 @@ PlatformAssembly=$(_PlatformAssemblyName).dll RelativeAppBundlePath=$(_RelativeAppBundlePath) Registrar=$(_BundlerRegistrar) + RequirePInvokeWrappers=$(_RequirePInvokeWrappers) RuntimeConfigurationFile=$(_RuntimeConfigurationFile) SdkDevPath=$(_SdkDevPath) SdkRootDirectory=$(_XamarinSdkRootDirectory) diff --git a/msbuild/Xamarin.MacDev.Tasks.Core/Tasks/ParseBundlerArgumentsTaskBase.cs b/msbuild/Xamarin.MacDev.Tasks.Core/Tasks/ParseBundlerArgumentsTaskBase.cs index 43f432399535..012845537ae8 100644 --- a/msbuild/Xamarin.MacDev.Tasks.Core/Tasks/ParseBundlerArgumentsTaskBase.cs +++ b/msbuild/Xamarin.MacDev.Tasks.Core/Tasks/ParseBundlerArgumentsTaskBase.cs @@ -43,6 +43,9 @@ public abstract class ParseBundlerArgumentsTaskBase : XamarinTask { [Output] public string Registrar { get; set; } + [Output] + public string RequirePInvokeWrappers { get; set; } + // This is input too [Output] public string NoStrip { get; set; } @@ -141,6 +144,9 @@ public override bool Execute () case "package-debug-symbols": PackageDebugSymbols = string.IsNullOrEmpty (value) ? "true" : value; break; + case "require-pinvoke-wrappers": + RequirePInvokeWrappers = string.IsNullOrEmpty (value) ? "true" : value; + break; case "registrar": value = hasValue ? value : nextValue; // requires a value, which might be the next option Registrar = value; diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets index 1bf3fdcd7775..d57ca0314dfe 100644 --- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets +++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets @@ -1780,6 +1780,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. + diff --git a/tools/common/Application.cs b/tools/common/Application.cs index 30d07342f922..ab12a7cb8476 100644 --- a/tools/common/Application.cs +++ b/tools/common/Application.cs @@ -611,8 +611,16 @@ public string Name { get { return Path.GetFileNameWithoutExtension (AppDirectory); } } + bool? requires_pinvoke_wrappers; public bool RequiresPInvokeWrappers { get { + if (requires_pinvoke_wrappers.HasValue) + return requires_pinvoke_wrappers.Value; + + // By default this is disabled for .NET + if (Driver.IsDotNet) + return false; + if (Platform == ApplePlatform.MacOSX) return false; @@ -624,6 +632,9 @@ public bool RequiresPInvokeWrappers { return MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.ThrowManagedException || MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.Abort; } + set { + requires_pinvoke_wrappers = value; + } } public string PlatformName { diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs index 60f725232cf9..ad8a4a8fe3c6 100644 --- a/tools/common/Driver.cs +++ b/tools/common/Driver.cs @@ -253,6 +253,9 @@ static bool ParseOptions (Application app, Mono.Options.OptionSet options, strin options.Add ("rid=", "The runtime identifier we're building for", v => { app.RuntimeIdentifier = v; }, true /* hidden - this is only for build-time --runregistrar support */); + options.Add ("require-pinvoke-wrappers:", v => { + app.RequiresPInvokeWrappers = ParseBool (v, "--require-pinvoke-wrappers"); + }); // Keep the ResponseFileSource option at the end. diff --git a/tools/dotnet-linker/LinkerConfiguration.cs b/tools/dotnet-linker/LinkerConfiguration.cs index a8363a78492a..572d72b23e78 100644 --- a/tools/dotnet-linker/LinkerConfiguration.cs +++ b/tools/dotnet-linker/LinkerConfiguration.cs @@ -57,6 +57,8 @@ public class LinkerConfiguration { Dictionary> msbuild_items = new Dictionary> (); + internal PInvokeWrapperGenerator PInvokeWrapperGenerationState; + public static LinkerConfiguration GetInstance (LinkContext context, bool createIfNotFound = true) { if (!configurations.TryGetValue (context, out var instance) && createIfNotFound) { @@ -231,6 +233,11 @@ public static LinkerConfiguration GetInstance (LinkContext context, bool createI case "Registrar": Application.ParseRegistrar (value); break; + case "RequirePInvokeWrappers": + if (!TryParseOptionalBoolean (value, out var require_pinvoke_wrappers)) + throw new InvalidOperationException ($"Unable to parse the {key} value: {value} in {linker_file}"); + Application.RequiresPInvokeWrappers = require_pinvoke_wrappers.Value; + break; case "RuntimeConfigurationFile": Application.RuntimeConfigurationFile = value; break; @@ -401,6 +408,7 @@ public void Write () Console.WriteLine ($" RelativeAppBundlePath: {RelativeAppBundlePath}"); Console.WriteLine ($" Registrar: {Application.Registrar} (Options: {Application.RegistrarOptions})"); Console.WriteLine ($" RuntimeConfigurationFile: {Application.RuntimeConfigurationFile}"); + Console.WriteLine ($" RequirePInvokeWrappers: {Application.RequiresPInvokeWrappers}"); Console.WriteLine ($" SdkDevPath: {Driver.SdkRoot}"); Console.WriteLine ($" SdkRootDirectory: {SdkRootDirectory}"); Console.WriteLine ($" SdkVersion: {SdkVersion}"); diff --git a/tools/dotnet-linker/Steps/GenerateMainStep.cs b/tools/dotnet-linker/Steps/GenerateMainStep.cs index 5d9d7cc550dd..485f1f972fad 100644 --- a/tools/dotnet-linker/Steps/GenerateMainStep.cs +++ b/tools/dotnet-linker/Steps/GenerateMainStep.cs @@ -55,6 +55,24 @@ protected override void TryEndProcess () if (app.EnableDebug) item.Metadata.Add ("Arguments", "-DDEBUG"); items.Add (item); + + if (app.RequiresPInvokeWrappers) { + var state = Configuration.PInvokeWrapperGenerationState; + if (state.Started) { + // The generator is 'started' by the linker, which means it may not + // be started if the linker was not executed due to re-using cached results. + state.End (); + } + item = new MSBuildItem { + Include = state.SourcePath, + Metadata = { + { "Arch", abi.AsArchString () }, + }, + }; + if (app.EnableDebug) + item.Metadata.Add ("Arguments", "-DDEBUG"); + items.Add (item); + } } Configuration.WriteOutputForMSBuild ("_MainFile", items); diff --git a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs index 465fcb2724df..193cf5117771 100644 --- a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs +++ b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs @@ -18,27 +18,56 @@ namespace Xamarin.Linker.Steps public class ListExportedSymbols : BaseStep { PInvokeWrapperGenerator state; +#if !NET bool skip_sdk_assemblies; +#endif + + PInvokeWrapperGenerator State { + get { +#if NET + if (state is null && DerivedLinkContext.App.RequiresPInvokeWrappers) { + Configuration.PInvokeWrapperGenerationState = new PInvokeWrapperGenerator () { + App = DerivedLinkContext.App, + SourcePath = Path.Combine (Configuration.CacheDirectory, "pinvokes.mm"), + HeaderPath = Path.Combine (Configuration.CacheDirectory, "pinvokes.h"), + Registrar = DerivedLinkContext.StaticRegistrar, + }; + state = Configuration.PInvokeWrapperGenerationState; + } +#endif + return state; + } + } + +#if NET + public LinkerConfiguration Configuration { + get { + return LinkerConfiguration.GetInstance (Context); + } + } +#endif public DerivedLinkContext DerivedLinkContext { get { #if NET - return LinkerConfiguration.GetInstance (Context).DerivedLinkContext; + return Configuration.DerivedLinkContext; #else return (DerivedLinkContext) Context; #endif } } - public ListExportedSymbols () : this (null) +#if NET + public ListExportedSymbols () { } - +#else internal ListExportedSymbols (PInvokeWrapperGenerator state, bool skip_sdk_assemblies = false) { this.state = state; this.skip_sdk_assemblies = skip_sdk_assemblies; } +#endif protected override void ProcessAssembly (AssemblyDefinition assembly) { @@ -64,23 +93,34 @@ protected override void ProcessAssembly (AssemblyDefinition assembly) if (!hasSymbols) return; + var modified = false; foreach (var type in assembly.MainModule.Types) - ProcessType (type); + modified |= ProcessType (type); + + // Make sure the linker saves any changes in the assembly. + if (modified) { + var action = Context.Annotations.GetAction (assembly); + if (action == AssemblyAction.Copy) + Context.Annotations.SetAction (assembly, AssemblyAction.Save); + } } - void ProcessType (TypeDefinition type) + bool ProcessType (TypeDefinition type) { + var modified = false; if (type.HasNestedTypes) { foreach (var nested in type.NestedTypes) - ProcessType (nested); + modified |= ProcessType (nested); } if (type.HasMethods) { foreach (var method in type.Methods) - ProcessMethod (method); + modified |= ProcessMethod (method); } AddRequiredObjectiveCType (type); + + return modified; } void AddRequiredObjectiveCType (TypeDefinition type) @@ -106,20 +146,23 @@ void AddRequiredObjectiveCType (TypeDefinition type) } } - void ProcessMethod (MethodDefinition method) + bool ProcessMethod (MethodDefinition method) { + var modified = false; + if (method.IsPInvokeImpl && method.HasPInvokeInfo && method.PInvokeInfo != null) { var pinfo = method.PInvokeInfo; bool addPInvokeSymbol = false; - if (state != null) { + if (State != null) { switch (pinfo.EntryPoint) { case "objc_msgSend": case "objc_msgSendSuper": case "objc_msgSend_stret": case "objc_msgSendSuper_stret": case "objc_msgSend_fpret": - state.ProcessMethod (method); + State.ProcessMethod (method); + modified = true; break; default: break; @@ -181,6 +224,8 @@ void ProcessMethod (MethodDefinition method) DerivedLinkContext.RequiredSymbols.AddField ((string) symbol).AddMember (property); } } + + return modified; } } }