Skip to content

Commit 25a2f65

Browse files
authored
[release/9.0.1xx] Don't fail metadata updates on missing assemblies (#40725) (#43393)
1 parent 7ffe7d4 commit 25a2f65

File tree

10 files changed

+166
-1
lines changed

10 files changed

+166
-1
lines changed

src/BuiltInTools/DotNetDeltaApplier/HotReloadAgent.cs

+20-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private UpdateHandlerActions GetMetadataUpdateHandlerActions()
8282
var handlerActions = new UpdateHandlerActions();
8383
foreach (var assembly in sortedAssemblies)
8484
{
85-
foreach (var attr in assembly.GetCustomAttributesData())
85+
foreach (var attr in TryGetCustomAttributesData(assembly))
8686
{
8787
// Look up the attribute by name rather than by type. This would allow netstandard targeting libraries to
8888
// define their own copy without having to cross-compile.
@@ -106,6 +106,25 @@ private UpdateHandlerActions GetMetadataUpdateHandlerActions()
106106
return handlerActions;
107107
}
108108

109+
private IList<CustomAttributeData> TryGetCustomAttributesData(Assembly assembly)
110+
{
111+
try
112+
{
113+
return assembly.GetCustomAttributesData();
114+
}
115+
catch (Exception e)
116+
{
117+
// In cross-platform scenarios, such as debugging in VS through WSL, Roslyn
118+
// runs on Windows, and the agent runs on Linux. Assemblies accessible to Windows
119+
// may not be available or loaded on linux (such as WPF's assemblies).
120+
// In such case, we can ignore the assemblies and continue enumerating handlers for
121+
// the rest of the assemblies of current domain.
122+
_log($"'{assembly.FullName}' is not loaded ({e.Message})");
123+
124+
return new List<CustomAttributeData>();
125+
}
126+
}
127+
109128
internal void GetHandlerActions(UpdateHandlerActions handlerActions, Type handlerType)
110129
{
111130
bool methodFound = false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\Dep\Dep.csproj" />
11+
</ItemGroup>
12+
13+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Diagnostics;
2+
using System.Reflection.Metadata;
3+
4+
[assembly: MetadataUpdateHandler(typeof(UpdateHandler))]
5+
6+
// delete the dependency dll to cause load failure of DepSubType
7+
var depPath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location!)!, "Dep2.dll");
8+
File.Delete(depPath);
9+
Console.WriteLine($"File deleted: {depPath}");
10+
11+
while (true)
12+
{
13+
lock (UpdateHandler.Guard)
14+
{
15+
Printer.Print();
16+
Dep.DepLib.F();
17+
}
18+
19+
Thread.Sleep(100);
20+
}
21+
22+
static class UpdateHandler
23+
{
24+
// Lock to avoid the updated Print method executing concurrently with the update handler.
25+
public static object Guard = new object();
26+
27+
public static void UpdateApplication(Type[] types)
28+
{
29+
lock (Guard)
30+
{
31+
Console.WriteLine($"Updated types: {(types == null ? "<null>" : types.Length == 0 ? "<empty>" : string.Join(",", types.Select(t => t.Name)))}");
32+
}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
public class DepType
4+
{
5+
int F() => 1;
6+
}
7+
8+
public class Printer
9+
{
10+
public static void Print()
11+
=> Console.WriteLine("Hello!");
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// This attribute is not causing Dep.dll to be loaded, but enough
2+
// to cause the HotReloadAgent to fail on getting custom attributes.
3+
[assembly: Dep2.Test()]
4+
5+
namespace Dep;
6+
7+
public class DepLib
8+
{
9+
public static void F()
10+
{
11+
Console.WriteLine(1);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="..\Dep2\Dep2.csproj" />
10+
</ItemGroup>
11+
12+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Dep2;
2+
3+
public class Dep2Lib
4+
{
5+
void F()
6+
{
7+
Console.WriteLine(1);
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
</PropertyGroup>
7+
8+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System;
2+
3+
namespace Dep2;
4+
5+
[AttributeUsage(AttributeTargets.Assembly)]
6+
public class TestAttribute : Attribute
7+
{
8+
}

test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs

+37
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,42 @@ public async Task BlazorWasm()
120120
//UpdateSourceFile(Path.Combine(testAsset.Path, "Pages", "Index.razor"), newSource);
121121
//await App.AssertOutputLineStartsWith(MessageDescriptor.HotReloadSucceeded);
122122
}
123+
124+
// Test is timing out on .NET Framework: https://github.com/dotnet/sdk/issues/41669
125+
[CoreMSBuildOnlyFact]
126+
public async Task HandleMissingAssemblyFailure()
127+
{
128+
var testAsset = TestAssets.CopyTestAsset("WatchAppMissingAssemblyFailure")
129+
.WithSource();
130+
131+
App.Start(testAsset, [], "App");
132+
133+
await App.AssertWaitingForChanges();
134+
135+
var newSrc = /* lang=c#-test */"""
136+
using System;
137+
138+
public class DepType
139+
{
140+
int F() => 1;
141+
}
142+
143+
public class Printer
144+
{
145+
public static void Print()
146+
=> Console.WriteLine("Updated!");
147+
}
148+
""";
149+
150+
// Delete all files in testAsset.Path named Dep.dll
151+
foreach (var depDll in Directory.GetFiles(testAsset.Path, "Dep2.dll", SearchOption.AllDirectories))
152+
{
153+
File.Delete(depDll);
154+
}
155+
156+
File.WriteAllText(Path.Combine(testAsset.Path, "App", "Update.cs"), newSrc);
157+
158+
await App.AssertOutputLineStartsWith("Updated types: Printer");
159+
}
123160
}
124161
}

0 commit comments

Comments
 (0)