Skip to content

Commit

Permalink
[release/9.0.1xx] Don't fail metadata updates on missing assemblies (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromelaban committed Sep 16, 2024
1 parent 7ffe7d4 commit 25a2f65
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 1 deletion.
21 changes: 20 additions & 1 deletion src/BuiltInTools/DotNetDeltaApplier/HotReloadAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private UpdateHandlerActions GetMetadataUpdateHandlerActions()
var handlerActions = new UpdateHandlerActions();
foreach (var assembly in sortedAssemblies)
{
foreach (var attr in assembly.GetCustomAttributesData())
foreach (var attr in TryGetCustomAttributesData(assembly))
{
// Look up the attribute by name rather than by type. This would allow netstandard targeting libraries to
// define their own copy without having to cross-compile.
Expand All @@ -106,6 +106,25 @@ private UpdateHandlerActions GetMetadataUpdateHandlerActions()
return handlerActions;
}

private IList<CustomAttributeData> TryGetCustomAttributesData(Assembly assembly)
{
try
{
return assembly.GetCustomAttributesData();
}
catch (Exception e)
{
// In cross-platform scenarios, such as debugging in VS through WSL, Roslyn
// runs on Windows, and the agent runs on Linux. Assemblies accessible to Windows
// may not be available or loaded on linux (such as WPF's assemblies).
// In such case, we can ignore the assemblies and continue enumerating handlers for
// the rest of the assemblies of current domain.
_log($"'{assembly.FullName}' is not loaded ({e.Message})");

return new List<CustomAttributeData>();
}
}

internal void GetHandlerActions(UpdateHandlerActions handlerActions, Type handlerType)
{
bool methodFound = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Dep\Dep.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Diagnostics;
using System.Reflection.Metadata;

[assembly: MetadataUpdateHandler(typeof(UpdateHandler))]

// delete the dependency dll to cause load failure of DepSubType
var depPath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location!)!, "Dep2.dll");
File.Delete(depPath);
Console.WriteLine($"File deleted: {depPath}");

while (true)
{
lock (UpdateHandler.Guard)
{
Printer.Print();
Dep.DepLib.F();
}

Thread.Sleep(100);
}

static class UpdateHandler
{
// Lock to avoid the updated Print method executing concurrently with the update handler.
public static object Guard = new object();

public static void UpdateApplication(Type[] types)
{
lock (Guard)
{
Console.WriteLine($"Updated types: {(types == null ? "<null>" : types.Length == 0 ? "<empty>" : string.Join(",", types.Select(t => t.Name)))}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

public class DepType
{
int F() => 1;
}

public class Printer
{
public static void Print()
=> Console.WriteLine("Hello!");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This attribute is not causing Dep.dll to be loaded, but enough
// to cause the HotReloadAgent to fail on getting custom attributes.
[assembly: Dep2.Test()]

namespace Dep;

public class DepLib
{
public static void F()
{
Console.WriteLine(1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Dep2\Dep2.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Dep2;

public class Dep2Lib
{
void F()
{
Console.WriteLine(1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace Dep2;

[AttributeUsage(AttributeTargets.Assembly)]
public class TestAttribute : Attribute
{
}
37 changes: 37 additions & 0 deletions test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,42 @@ public async Task BlazorWasm()
//UpdateSourceFile(Path.Combine(testAsset.Path, "Pages", "Index.razor"), newSource);
//await App.AssertOutputLineStartsWith(MessageDescriptor.HotReloadSucceeded);
}

// Test is timing out on .NET Framework: https://github.com/dotnet/sdk/issues/41669
[CoreMSBuildOnlyFact]
public async Task HandleMissingAssemblyFailure()
{
var testAsset = TestAssets.CopyTestAsset("WatchAppMissingAssemblyFailure")
.WithSource();

App.Start(testAsset, [], "App");

await App.AssertWaitingForChanges();

var newSrc = /* lang=c#-test */"""
using System;

public class DepType
{
int F() => 1;
}

public class Printer
{
public static void Print()
=> Console.WriteLine("Updated!");
}
""";

// Delete all files in testAsset.Path named Dep.dll
foreach (var depDll in Directory.GetFiles(testAsset.Path, "Dep2.dll", SearchOption.AllDirectories))
{
File.Delete(depDll);
}

File.WriteAllText(Path.Combine(testAsset.Path, "App", "Update.cs"), newSrc);

await App.AssertOutputLineStartsWith("Updated types: Printer");
}
}
}

0 comments on commit 25a2f65

Please sign in to comment.