Skip to content

Commit 826cf5b

Browse files
committed
Rewrite Mono.Android.Runtime.dll if marshal methods are enabled
The rewrite changes visibility of the `Android.Runtime.AndroidEnvironmentInternal` type from `internal` to `public` after all the linking and marshal method processing is done.
1 parent 0ccd361 commit 826cf5b

File tree

6 files changed

+185
-31
lines changed

6 files changed

+185
-31
lines changed

src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Android.Runtime
44
{
5-
public static class AndroidEnvironmentInternal
5+
internal static class AndroidEnvironmentInternal
66
{
77
internal static Action<Exception>? UnhandledExceptionHandler;
88

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Microsoft.Android.Build.Tasks;
5+
using Microsoft.Build.Framework;
6+
7+
namespace Xamarin.Android.Tasks;
8+
9+
public class FixUpMonoAndroidRuntime : AndroidTask
10+
{
11+
public override string TaskPrefix => "FUMAR";
12+
13+
[Required]
14+
public string IntermediateOutputDirectory { get; set; } = String.Empty;
15+
16+
[Required]
17+
public ITaskItem[] ResolvedAssemblies { get; set; } = [];
18+
19+
public override bool RunTask ()
20+
{
21+
List<ITaskItem> monoAndroidRuntimeItems = new ();
22+
foreach (ITaskItem item in ResolvedAssemblies) {
23+
24+
if (!MonoAndroidHelper.StringEquals (Path.GetFileName (item.ItemSpec), "Mono.Android.Runtime.dll", StringComparison.OrdinalIgnoreCase)) {
25+
continue;
26+
}
27+
monoAndroidRuntimeItems.Add (item);
28+
}
29+
30+
if (monoAndroidRuntimeItems.Count == 0) {
31+
Log.LogDebugMessage ("No 'Mono.Android.Runtime.dll' items found");
32+
return !Log.HasLoggedErrors;
33+
}
34+
35+
return MonoAndroidRuntimeMarshalMethodsFixUp.Run (Log, monoAndroidRuntimeItems) && !Log.HasLoggedErrors;
36+
}
37+
}

src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -179,39 +179,13 @@ public void Rewrite (bool brokenExceptionTransitions)
179179
void CopyFile (string source, string target)
180180
{
181181
log.LogDebugMessage ($"[{targetArch}] Copying rewritten assembly: {source} -> {target}");
182-
183-
string targetBackup = $"{target}.bak";
184-
if (File.Exists (target)) {
185-
// Try to avoid sharing violations by first renaming the target
186-
File.Move (target, targetBackup);
187-
}
188-
189-
File.Copy (source, target, true);
190-
191-
if (File.Exists (targetBackup)) {
192-
try {
193-
File.Delete (targetBackup);
194-
} catch (Exception ex) {
195-
// On Windows the deletion may fail, depending on lock state of the original `target` file before the move.
196-
log.LogDebugMessage ($"[{targetArch}] While trying to delete '{targetBackup}', exception was thrown: {ex}");
197-
log.LogDebugMessage ($"[{targetArch}] Failed to delete backup file '{targetBackup}', ignoring.");
198-
}
199-
}
182+
MonoAndroidHelper.CopyFileAvoidSharingViolations (log, source, target);
200183
}
201184

202185
void RemoveFile (string? path)
203186
{
204-
if (String.IsNullOrEmpty (path) || !File.Exists (path)) {
205-
return;
206-
}
207-
208-
try {
209-
log.LogDebugMessage ($"[{targetArch}] Deleting: {path}");
210-
File.Delete (path);
211-
} catch (Exception ex) {
212-
log.LogWarning ($"[{targetArch}] Unable to delete source file '{path}'");
213-
log.LogDebugMessage ($"[{targetArch}] {ex.ToString ()}");
214-
}
187+
log.LogDebugMessage ($"[{targetArch}] Deleting: {path}");
188+
MonoAndroidHelper.TryRemoveFile (log, path);
215189
}
216190

217191
static bool HasUnmanagedCallersOnlyAttribute (MethodDefinition method)
@@ -504,7 +478,7 @@ MethodDefinition GetUnmanagedCallersOnlyAttributeConstructor (IAssemblyResolver
504478
AssemblyDefinition? asm = resolver.Resolve ("System.Runtime.InteropServices");
505479
if (asm == null)
506480
throw new ArgumentNullException (nameof (asm));
507-
481+
508482
TypeDefinition? unmanagedCallersOnlyAttribute = null;
509483
foreach (ModuleDefinition md in asm.Modules) {
510484
foreach (ExportedType et in md.ExportedTypes) {

src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,5 +874,40 @@ public static void LogTextStreamContents (TaskLoggingHelper log, string message,
874874
log.LogDebugMessage (message);
875875
log.LogDebugMessage (reader.ReadToEnd ());
876876
}
877+
878+
public static void CopyFileAvoidSharingViolations (TaskLoggingHelper log, string source, string dest)
879+
{
880+
string destBackup = $"{dest}.bak";
881+
if (File.Exists (dest)) {
882+
// Try to avoid sharing violations by first renaming the target
883+
File.Move (dest, destBackup);
884+
}
885+
886+
File.Copy (source, dest, true);
887+
888+
if (File.Exists (destBackup)) {
889+
try {
890+
File.Delete (destBackup);
891+
} catch (Exception ex) {
892+
// On Windows the deletion may fail, depending on lock state of the original `target` file before the move.
893+
log.LogDebugMessage ($"While trying to delete '{destBackup}', exception was thrown: {ex}");
894+
log.LogDebugMessage ($"Failed to delete backup file '{destBackup}', ignoring.");
895+
}
896+
}
897+
}
898+
899+
public static void TryRemoveFile (TaskLoggingHelper log, string? filePath)
900+
{
901+
if (String.IsNullOrEmpty (filePath) || !File.Exists (filePath)) {
902+
return;
903+
}
904+
905+
try {
906+
File.Delete (filePath);
907+
} catch (Exception ex) {
908+
log.LogWarning ($"Unable to delete source file '{filePath}'");
909+
log.LogDebugMessage ($"{ex.ToString ()}");
910+
}
911+
}
877912
}
878913
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using Microsoft.Android.Build.Tasks;
4+
using Microsoft.Build.Framework;
5+
using Microsoft.Build.Utilities;
6+
using Mono.Cecil;
7+
8+
namespace Xamarin.Android.Tasks;
9+
10+
class MonoAndroidRuntimeMarshalMethodsFixUp
11+
{
12+
const string RuntimeTypeName = "Android.Runtime.AndroidEnvironmentInternal";
13+
14+
public static bool Run (TaskLoggingHelper log, List<ITaskItem> items)
15+
{
16+
bool everythingWorked = true;
17+
foreach (ITaskItem item in items) {
18+
if (!ApplyFixUp (log, item)) {
19+
everythingWorked = false;
20+
}
21+
}
22+
23+
return everythingWorked;
24+
}
25+
26+
static bool ApplyFixUp (TaskLoggingHelper log, ITaskItem monoAndroidRuntime)
27+
{
28+
string newDirPath = Path.Combine (Path.GetDirectoryName (monoAndroidRuntime.ItemSpec), "new");
29+
string newFilePath = Path.Combine (newDirPath, Path.GetFileName (monoAndroidRuntime.ItemSpec));
30+
Directory.CreateDirectory (newDirPath);
31+
32+
string origPdbPath = Path.ChangeExtension (monoAndroidRuntime.ItemSpec, ".pdb");
33+
bool havePdb = File.Exists (origPdbPath);
34+
35+
log.LogDebugMessage ($"Fixing up {monoAndroidRuntime.ItemSpec}");
36+
var readerParams = new ReaderParameters () {
37+
InMemory = true,
38+
ReadSymbols = havePdb,
39+
};
40+
AssemblyDefinition asmdef = AssemblyDefinition.ReadAssembly (monoAndroidRuntime.ItemSpec, readerParams);
41+
TypeDefinition? androidRuntimeInternal = null;
42+
foreach (ModuleDefinition module in asmdef.Modules) {
43+
androidRuntimeInternal = FindAndroidRuntimeInternal (module);
44+
if (androidRuntimeInternal != null) {
45+
break;
46+
}
47+
}
48+
49+
if (androidRuntimeInternal == null) {
50+
log.LogDebugMessage ($"'{RuntimeTypeName}' not found in {monoAndroidRuntime.ItemSpec}");
51+
return true; // Not an error, per se...
52+
}
53+
log.LogDebugMessage ($"Found '{RuntimeTypeName}', making it public");
54+
androidRuntimeInternal.IsPublic = true;
55+
56+
var writerParams = new WriterParameters {
57+
WriteSymbols = havePdb,
58+
};
59+
asmdef.Write (newFilePath, writerParams);
60+
61+
CopyFile (log, newFilePath, monoAndroidRuntime.ItemSpec);
62+
RemoveFile (log, newFilePath);
63+
64+
if (!havePdb) {
65+
return true;
66+
}
67+
68+
string pdbPath = Path.ChangeExtension (newFilePath, ".pdb");
69+
havePdb = File.Exists (pdbPath);
70+
if (!havePdb) {
71+
return true;
72+
}
73+
74+
CopyFile (log, pdbPath, origPdbPath);
75+
RemoveFile (log, pdbPath);
76+
77+
return true;
78+
}
79+
80+
static void CopyFile (TaskLoggingHelper log, string source, string target)
81+
{
82+
log.LogDebugMessage ($"Copying rewritten assembly: {source} -> {target}");
83+
MonoAndroidHelper.CopyFileAvoidSharingViolations (log, source, target);
84+
}
85+
86+
static void RemoveFile (TaskLoggingHelper log, string? path)
87+
{
88+
log.LogDebugMessage ($"Deleting: {path}");
89+
MonoAndroidHelper.TryRemoveFile (log, path);
90+
}
91+
92+
static TypeDefinition? FindAndroidRuntimeInternal (ModuleDefinition module)
93+
{
94+
foreach (TypeDefinition t in module.Types) {
95+
if (MonoAndroidHelper.StringEquals (RuntimeTypeName, t.FullName)) {
96+
return t;
97+
}
98+
}
99+
100+
return null;
101+
}
102+
}

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
103103
<UsingTask TaskName="Xamarin.Android.Tasks.UnzipToFolder" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
104104
<UsingTask TaskName="Xamarin.Android.Tasks.GenerateJniRemappingNativeCode" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
105105
<UsingTask TaskName="Xamarin.Android.Tasks.PrepareSatelliteAssemblies" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
106+
<UsingTask TaskName="Xamarin.Android.Tasks.FixUpMonoAndroidRuntime" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
106107
<!--
107108
*******************************************
108109
Extensibility hook that allows VS to
@@ -1605,6 +1606,11 @@ because xbuild doesn't support framework reference assemblies.
16051606
EnableNativeRuntimeLinking="$(_AndroidEnableNativeRuntimeLinking)">
16061607
</GenerateJavaStubs>
16071608

1609+
<FixUpMonoAndroidRuntime
1610+
Condition=" '$(_AndroidUseMarshalMethods)' == 'true' And '$(AndroidIncludeDebugSymbols)' == 'false' "
1611+
IntermediateOutputDirectory="$(IntermediateOutputPath)"
1612+
ResolvedAssemblies="@(_ResolvedAssemblies)" />
1613+
16081614
<RewriteMarshalMethods
16091615
Condition=" '$(_AndroidUseMarshalMethods)' == 'true' And '$(AndroidIncludeDebugSymbols)' == 'false' "
16101616
EnableManagedMarshalMethodsLookup="$(_AndroidUseManagedMarshalMethodsLookup)"

0 commit comments

Comments
 (0)