diff --git a/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs b/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs index 198e6c92423..d64dadba305 100644 --- a/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs +++ b/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs @@ -6,7 +6,7 @@ internal static class AndroidEnvironmentInternal { internal static Action? UnhandledExceptionHandler; - internal static void UnhandledException (Exception e) + public static void UnhandledException (Exception e) { if (UnhandledExceptionHandler == null) { return; diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/FixUpMonoAndroidRuntime.cs b/src/Xamarin.Android.Build.Tasks/Tasks/FixUpMonoAndroidRuntime.cs new file mode 100644 index 00000000000..5a8c3a413d7 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/FixUpMonoAndroidRuntime.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Framework; + +namespace Xamarin.Android.Tasks; + +public class FixUpMonoAndroidRuntime : AndroidTask +{ + public override string TaskPrefix => "FUMAR"; + + [Required] + public string IntermediateOutputDirectory { get; set; } = String.Empty; + + [Required] + public ITaskItem[] ResolvedAssemblies { get; set; } = []; + + public override bool RunTask () + { + List monoAndroidRuntimeItems = new (); + foreach (ITaskItem item in ResolvedAssemblies) { + + if (!MonoAndroidHelper.StringEquals (Path.GetFileName (item.ItemSpec), "Mono.Android.Runtime.dll", StringComparison.OrdinalIgnoreCase)) { + continue; + } + monoAndroidRuntimeItems.Add (item); + } + + if (monoAndroidRuntimeItems.Count == 0) { + Log.LogDebugMessage ("No 'Mono.Android.Runtime.dll' items found"); + return !Log.HasLoggedErrors; + } + + return MonoAndroidRuntimeMarshalMethodsFixUp.Run (Log, monoAndroidRuntimeItems) && !Log.HasLoggedErrors; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs index 807632241ba..d63bcc0b104 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs @@ -179,39 +179,13 @@ public void Rewrite (bool brokenExceptionTransitions) void CopyFile (string source, string target) { log.LogDebugMessage ($"[{targetArch}] Copying rewritten assembly: {source} -> {target}"); - - string targetBackup = $"{target}.bak"; - if (File.Exists (target)) { - // Try to avoid sharing violations by first renaming the target - File.Move (target, targetBackup); - } - - File.Copy (source, target, true); - - if (File.Exists (targetBackup)) { - try { - File.Delete (targetBackup); - } catch (Exception ex) { - // On Windows the deletion may fail, depending on lock state of the original `target` file before the move. - log.LogDebugMessage ($"[{targetArch}] While trying to delete '{targetBackup}', exception was thrown: {ex}"); - log.LogDebugMessage ($"[{targetArch}] Failed to delete backup file '{targetBackup}', ignoring."); - } - } + MonoAndroidHelper.CopyFileAvoidSharingViolations (log, source, target); } void RemoveFile (string? path) { - if (String.IsNullOrEmpty (path) || !File.Exists (path)) { - return; - } - - try { - log.LogDebugMessage ($"[{targetArch}] Deleting: {path}"); - File.Delete (path); - } catch (Exception ex) { - log.LogWarning ($"[{targetArch}] Unable to delete source file '{path}'"); - log.LogDebugMessage ($"[{targetArch}] {ex.ToString ()}"); - } + log.LogDebugMessage ($"[{targetArch}] Deleting: {path}"); + MonoAndroidHelper.TryRemoveFile (log, path); } static bool HasUnmanagedCallersOnlyAttribute (MethodDefinition method) @@ -504,7 +478,7 @@ MethodDefinition GetUnmanagedCallersOnlyAttributeConstructor (IAssemblyResolver AssemblyDefinition? asm = resolver.Resolve ("System.Runtime.InteropServices"); if (asm == null) throw new ArgumentNullException (nameof (asm)); - + TypeDefinition? unmanagedCallersOnlyAttribute = null; foreach (ModuleDefinition md in asm.Modules) { foreach (ExportedType et in md.ExportedTypes) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 13102b06955..bf339554775 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -874,5 +874,40 @@ public static void LogTextStreamContents (TaskLoggingHelper log, string message, log.LogDebugMessage (message); log.LogDebugMessage (reader.ReadToEnd ()); } + + public static void CopyFileAvoidSharingViolations (TaskLoggingHelper log, string source, string dest) + { + string destBackup = $"{dest}.bak"; + if (File.Exists (dest)) { + // Try to avoid sharing violations by first renaming the target + File.Move (dest, destBackup); + } + + File.Copy (source, dest, true); + + if (File.Exists (destBackup)) { + try { + File.Delete (destBackup); + } catch (Exception ex) { + // On Windows the deletion may fail, depending on lock state of the original `target` file before the move. + log.LogDebugMessage ($"While trying to delete '{destBackup}', exception was thrown: {ex}"); + log.LogDebugMessage ($"Failed to delete backup file '{destBackup}', ignoring."); + } + } + } + + public static void TryRemoveFile (TaskLoggingHelper log, string? filePath) + { + if (String.IsNullOrEmpty (filePath) || !File.Exists (filePath)) { + return; + } + + try { + File.Delete (filePath); + } catch (Exception ex) { + log.LogWarning ($"Unable to delete source file '{filePath}'"); + log.LogDebugMessage ($"{ex.ToString ()}"); + } + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidRuntimeMarshalMethodsFixUp.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidRuntimeMarshalMethodsFixUp.cs new file mode 100644 index 00000000000..4bdff04800c --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidRuntimeMarshalMethodsFixUp.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.IO; +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Mono.Cecil; + +namespace Xamarin.Android.Tasks; + +class MonoAndroidRuntimeMarshalMethodsFixUp +{ + const string RuntimeTypeName = "Android.Runtime.AndroidEnvironmentInternal"; + + public static bool Run (TaskLoggingHelper log, List items) + { + bool everythingWorked = true; + foreach (ITaskItem item in items) { + if (!ApplyFixUp (log, item)) { + everythingWorked = false; + } + } + + return everythingWorked; + } + + static bool ApplyFixUp (TaskLoggingHelper log, ITaskItem monoAndroidRuntime) + { + string newDirPath = Path.Combine (Path.GetDirectoryName (monoAndroidRuntime.ItemSpec), "new"); + string newFilePath = Path.Combine (newDirPath, Path.GetFileName (monoAndroidRuntime.ItemSpec)); + Directory.CreateDirectory (newDirPath); + + string origPdbPath = Path.ChangeExtension (monoAndroidRuntime.ItemSpec, ".pdb"); + bool havePdb = File.Exists (origPdbPath); + + log.LogDebugMessage ($"Fixing up {monoAndroidRuntime.ItemSpec}"); + var readerParams = new ReaderParameters () { + InMemory = true, + ReadSymbols = havePdb, + }; + AssemblyDefinition asmdef = AssemblyDefinition.ReadAssembly (monoAndroidRuntime.ItemSpec, readerParams); + TypeDefinition? androidRuntimeInternal = null; + foreach (ModuleDefinition module in asmdef.Modules) { + androidRuntimeInternal = FindAndroidRuntimeInternal (module); + if (androidRuntimeInternal != null) { + break; + } + } + + if (androidRuntimeInternal == null) { + log.LogDebugMessage ($"'{RuntimeTypeName}' not found in {monoAndroidRuntime.ItemSpec}"); + return true; // Not an error, per se... + } + log.LogDebugMessage ($"Found '{RuntimeTypeName}', making it public"); + androidRuntimeInternal.IsPublic = true; + + var writerParams = new WriterParameters { + WriteSymbols = havePdb, + }; + asmdef.Write (newFilePath, writerParams); + + CopyFile (log, newFilePath, monoAndroidRuntime.ItemSpec); + RemoveFile (log, newFilePath); + + if (!havePdb) { + return true; + } + + string pdbPath = Path.ChangeExtension (newFilePath, ".pdb"); + havePdb = File.Exists (pdbPath); + if (!havePdb) { + return true; + } + + CopyFile (log, pdbPath, origPdbPath); + RemoveFile (log, pdbPath); + + return true; + } + + static void CopyFile (TaskLoggingHelper log, string source, string target) + { + log.LogDebugMessage ($"Copying rewritten assembly: {source} -> {target}"); + MonoAndroidHelper.CopyFileAvoidSharingViolations (log, source, target); + } + + static void RemoveFile (TaskLoggingHelper log, string? path) + { + log.LogDebugMessage ($"Deleting: {path}"); + MonoAndroidHelper.TryRemoveFile (log, path); + } + + static TypeDefinition? FindAndroidRuntimeInternal (ModuleDefinition module) + { + foreach (TypeDefinition t in module.Types) { + if (MonoAndroidHelper.StringEquals (RuntimeTypeName, t.FullName)) { + return t; + } + } + + return null; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index ee24d3a843d..a33f0dfe319 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -103,6 +103,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. +