Skip to content

Commit efa14e2

Browse files
[Xamarin.Android.Build.Tasks] MarshalMethodsAssemblyRewriter+new file (#8151)
Context: dotnet/maui#15399 (comment) During a PR updating AndroidX dependencies in .NET MAUI, we saw: Task GenerateJavaStubs … System.IO.IOException: The process cannot access the file 'D:\a\_work\1\s\src\Compatibility\ControlGallery\src\Android\obj\Release\net8.0-android\android\assets\Xamarin.AndroidX.CustomView.pdb' because it is being used by another process. at System.IO.FileSystem.CopyFile(String sourceFullPath, String destFullPath, Boolean overwrite) at Xamarin.Android.Tasks.MarshalMethodsAssemblyRewriter.Rewrite(DirectoryAssemblyResolver resolver, List`1 targetAssemblyPaths, Boolean brokenExceptionTransitions) at Xamarin.Android.Tasks.GenerateJavaStubs.Run(DirectoryAssemblyResolver res, Boolean useMarshalMethods) at Xamarin.Android.Tasks.GenerateJavaStubs.RunTask() at Microsoft.Android.Build.Tasks.AndroidTask.Execute() Which has some very odd logging right before the failure: Copying rewritten assembly: obj\Release\net8.0-android\android\assets\Xamarin.AndroidX.CustomView.PoolingContainer.dll.new -> obj\Release\net8.0-android\android\assets\Xamarin.AndroidX.CustomView.PoolingContainer.dll Copying rewritten assembly: obj\Release\net8.0-android\android\assets\Xamarin.AndroidX.CustomView.PoolingContainer.pdb -> obj\Release\net8.0-android\android\assets\Xamarin.AndroidX.CustomView.pdb Copying `Xamarin.AndroidX.CustomView.PoolingContainer.pdb` to `Xamarin.AndroidX.CustomView.pdb`? Where is `.PoolingContainer`? I could reproduce the issue in a test with `$(PublishTrimmed)`=False: [Test] public void SimilarAndroidXAssemblyNames ([Values(true, false)] bool publishTrimmed) { var proj = new XamarinAndroidApplicationProject { IsRelease = true, AotAssemblies = publishTrimmed, PackageReferences = { new Package { Id = "Xamarin.AndroidX.CustomView", Version = "1.1.0.17" }, new Package { Id = "Xamarin.AndroidX.CustomView.PoolingContainer", Version = "1.0.0.4" }, } }; proj.SetProperty (KnownProperties.PublishTrimmed, publishTrimmed.ToString()); proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}", "AndroidX.CustomView.PoolingContainer.PoolingContainer.IsPoolingContainer (null);"); using var builder = CreateApkBuilder (); Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); } In `MarshalMethodsAssemblyRewriter` we write temporary assembly files to `foo.new` and copy to the original path at `foo.dll`. We next copy any symbol files if found. I found two underlying issues here: First, this `Mono.Cecil` API: AssemblyDefinition.Write("foo.new", new WriterParameters { WriteSymbols = true, }); would write a `foo.pdb` instead of `foo.new.pdb`, and so we have no way for this to write symbols to a temporary location. I put the temporary location in a `new` subdirectory instead of appending `.new` to the path. The second problem is this code: target = Path.ChangeExtension (Path.Combine (targetPath, Path.GetFileNameWithoutExtension (pdb)), ".pdb"); CopyFile (pdb, target); It appears to lose `.PoolingContainer` from the path, and so it uses the destination of: obj\Release\net8.0-android\android\assets\Xamarin.AndroidX.CustomView.pdb By using a `new` subdirectory instead, we bypass this issue. After these changes, we instead get: Copying rewritten assembly: obj\Release\android\assets\new\Xamarin.AndroidX.CustomView.PoolingContainer.dll -> obj\Release\android\assets\Xamarin.AndroidX.CustomView.PoolingContainer.dll Copying rewritten assembly: obj\Release\android\assets\new\Xamarin.AndroidX.CustomView.PoolingContainer.pdb -> obj\Release\android\assets\Xamarin.AndroidX.CustomView.PoolingContainer.pdb Deleting: obj\Release\android\assets\new\Xamarin.AndroidX.CustomView.PoolingContainer.dll Deleting: obj\Release\android\assets\new\Xamarin.AndroidX.CustomView.PoolingContainer.pdb Lastly, I removed `.mdb` file support -- but that is completely unrelated to the issue.
1 parent 27078a5 commit efa14e2

File tree

2 files changed

+25
-19
lines changed

2 files changed

+25
-19
lines changed

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs

+17
Original file line numberDiff line numberDiff line change
@@ -2560,5 +2560,22 @@ public void CheckLintResourceFileReferencesAreFixed ()
25602560
StringAssertEx.DoesNotContain (errorFilePath, b.LastBuildOutput, $"Path {errorFilePath} should have been replaced.");
25612561
}
25622562
}
2563+
2564+
[Test]
2565+
public void SimilarAndroidXAssemblyNames ([Values(true, false)] bool publishTrimmed)
2566+
{
2567+
var proj = new XamarinAndroidApplicationProject {
2568+
IsRelease = true,
2569+
AotAssemblies = publishTrimmed,
2570+
PackageReferences = {
2571+
new Package { Id = "Xamarin.AndroidX.CustomView", Version = "1.1.0.17" },
2572+
new Package { Id = "Xamarin.AndroidX.CustomView.PoolingContainer", Version = "1.0.0.4" },
2573+
}
2574+
};
2575+
proj.SetProperty (KnownProperties.PublishTrimmed, publishTrimmed.ToString());
2576+
proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}", "AndroidX.CustomView.PoolingContainer.PoolingContainer.IsPoolingContainer (null);");
2577+
using var builder = CreateApkBuilder ();
2578+
Assert.IsTrue (builder.Build (proj), "Build should have succeeded.");
2579+
}
25632580
}
25642581
}

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

+8-19
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,12 @@ public void Rewrite (DirectoryAssemblyResolver resolver, List<string> targetAsse
118118
foreach (AssemblyDefinition asm in uniqueAssemblies) {
119119
foreach (string path in GetAssemblyPaths (asm)) {
120120
var writerParams = new WriterParameters {
121-
WriteSymbols = (File.Exists (path + ".mdb") || File.Exists (Path.ChangeExtension (path, ".pdb"))),
121+
WriteSymbols = File.Exists (Path.ChangeExtension (path, ".pdb")),
122122
};
123123

124-
125-
string output = $"{path}.new";
124+
string directory = Path.Combine (Path.GetDirectoryName (path), "new");
125+
Directory.CreateDirectory (directory);
126+
string output = Path.Combine (directory, Path.GetFileName (path));
126127
log.LogDebugMessage ($"Writing new version of assembly: {output}");
127128

128129
// TODO: this should be used eventually, but it requires that all the types are reloaded from the assemblies before typemaps are generated
@@ -137,36 +138,23 @@ public void Rewrite (DirectoryAssemblyResolver resolver, List<string> targetAsse
137138
// versions around.
138139
foreach (string path in newAssemblyPaths) {
139140
string? pdb = null;
140-
string? mdb = null;
141141

142-
string source = Path.ChangeExtension (Path.Combine (Path.GetDirectoryName (path), Path.GetFileNameWithoutExtension (path)), ".pdb");
142+
string source = Path.ChangeExtension (path, ".pdb");
143143
if (File.Exists (source)) {
144144
pdb = source;
145145
}
146146

147-
source = $"{path}.mdb";
148-
if (File.Exists (source)) {
149-
mdb = source;
150-
}
151-
152147
foreach (string targetPath in targetAssemblyPaths) {
153-
string target = Path.Combine (targetPath, Path.GetFileNameWithoutExtension (path));
148+
string target = Path.Combine (targetPath, Path.GetFileName (path));
154149
CopyFile (path, target);
155150

156151
if (!String.IsNullOrEmpty (pdb)) {
157-
target = Path.ChangeExtension (Path.Combine (targetPath, Path.GetFileNameWithoutExtension (pdb)), ".pdb");
158-
CopyFile (pdb, target);
159-
}
160-
161-
if (!String.IsNullOrEmpty (mdb)) {
162-
target = Path.Combine (targetPath, Path.ChangeExtension (Path.GetFileName (path), ".mdb"));
163-
CopyFile (mdb, target);
152+
CopyFile (pdb, Path.ChangeExtension (target, ".pdb"));
164153
}
165154
}
166155

167156
RemoveFile (path);
168157
RemoveFile (pdb);
169-
RemoveFile (mdb);
170158
}
171159

172160
void CopyFile (string source, string target)
@@ -182,6 +170,7 @@ void RemoveFile (string? path)
182170
}
183171

184172
try {
173+
log.LogDebugMessage ($"Deleting: {path}");
185174
File.Delete (path);
186175
} catch (Exception ex) {
187176
log.LogWarning ($"Unable to delete source file '{path}'");

0 commit comments

Comments
 (0)