From 704c791b32fc43a9bf3a885afee6449d26646e03 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Sun, 20 Sep 2026 00:40:40 -0400 Subject: [PATCH] Fix AssemblyChecker dependency resolution Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- eng/Subsets.props | 2 ++ .../AssemblyChecker.Tests.csproj | 17 ++++++++++++++ .../AssemblyInspectorTests.cs | 16 ++++++++++++++ .../AssemblyWithExternalAttribute.csproj | 19 ++++++++++++++++ .../AssemblyWithExternalAttribute/Marker.cs | 12 ++++++++++ .../ExternalAttribute.csproj | 12 ++++++++++ .../ReferencedAssemblyAttribute.cs | 11 ++++++++++ .../AssemblyChecker/AssemblyChecker.csproj | 1 + .../AssemblyChecker/AssemblyInspector.cs | 22 +++++++++++++------ 9 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 src/coreclr/tools/AssemblyChecker.Tests/AssemblyChecker.Tests.csproj create mode 100644 src/coreclr/tools/AssemblyChecker.Tests/AssemblyInspectorTests.cs create mode 100644 src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/AssemblyWithExternalAttribute.csproj create mode 100644 src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/Marker.cs create mode 100644 src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ExternalAttribute.csproj create mode 100644 src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ReferencedAssemblyAttribute.cs diff --git a/eng/Subsets.props b/eng/Subsets.props index e16c5fb861462b..7cabb79e2a24b1 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -515,6 +515,8 @@ + + + AssemblyChecker.Tests + Debug;Release;Checked + false + $(NetCoreAppToolCurrent) + + + + + + + + + + + diff --git a/src/coreclr/tools/AssemblyChecker.Tests/AssemblyInspectorTests.cs b/src/coreclr/tools/AssemblyChecker.Tests/AssemblyInspectorTests.cs new file mode 100644 index 00000000000000..b9abf582060388 --- /dev/null +++ b/src/coreclr/tools/AssemblyChecker.Tests/AssemblyInspectorTests.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using AssemblyChecker.Tests.AssemblyWithExternalAttribute; +using Xunit; + +namespace AssemblyChecker.Tests; + +public class AssemblyInspectorTests +{ + [Fact] + public void IsDebugResolvesAssemblyLevelAttributeFromAdjacentAssembly() + { + Assert.False(AssemblyInspector.IsDebug(typeof(Marker).Assembly.Location)); + } +} diff --git a/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/AssemblyWithExternalAttribute.csproj b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/AssemblyWithExternalAttribute.csproj new file mode 100644 index 00000000000000..39bc304fb20bcc --- /dev/null +++ b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/AssemblyWithExternalAttribute.csproj @@ -0,0 +1,19 @@ + + + false + none + false + true + Library + true + $(NetCoreAppToolCurrent) + + + + + + + + + + diff --git a/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/Marker.cs b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/Marker.cs new file mode 100644 index 00000000000000..f8b20b2eb1b3ba --- /dev/null +++ b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/AssemblyWithExternalAttribute/Marker.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using AssemblyChecker.Tests.ExternalAttribute; + +[assembly: ReferencedAssembly] + +namespace AssemblyChecker.Tests.AssemblyWithExternalAttribute; + +public sealed class Marker +{ +} diff --git a/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ExternalAttribute.csproj b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ExternalAttribute.csproj new file mode 100644 index 00000000000000..fec6cef0fecb26 --- /dev/null +++ b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ExternalAttribute.csproj @@ -0,0 +1,12 @@ + + + false + Library + true + $(NetCoreAppToolCurrent) + + + + + + diff --git a/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ReferencedAssemblyAttribute.cs b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ReferencedAssemblyAttribute.cs new file mode 100644 index 00000000000000..e89de62bc23728 --- /dev/null +++ b/src/coreclr/tools/AssemblyChecker.Tests/TestAssets/ExternalAttribute/ReferencedAssemblyAttribute.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace AssemblyChecker.Tests.ExternalAttribute; + +[AttributeUsage(AttributeTargets.Assembly)] +public sealed class ReferencedAssemblyAttribute : Attribute +{ +} diff --git a/src/coreclr/tools/AssemblyChecker/AssemblyChecker.csproj b/src/coreclr/tools/AssemblyChecker/AssemblyChecker.csproj index 8d4f3d12c46d50..4e83af7632bade 100644 --- a/src/coreclr/tools/AssemblyChecker/AssemblyChecker.csproj +++ b/src/coreclr/tools/AssemblyChecker/AssemblyChecker.csproj @@ -15,6 +15,7 @@ + diff --git a/src/coreclr/tools/AssemblyChecker/AssemblyInspector.cs b/src/coreclr/tools/AssemblyChecker/AssemblyInspector.cs index e48b1d7ba52e66..bfe286a3905e57 100644 --- a/src/coreclr/tools/AssemblyChecker/AssemblyInspector.cs +++ b/src/coreclr/tools/AssemblyChecker/AssemblyInspector.cs @@ -3,17 +3,19 @@ using System.Diagnostics; using System.Reflection; +using System.Runtime.InteropServices; namespace AssemblyChecker { internal static class AssemblyInspector { - private static readonly RuntimeAssemblyResolver s_resolver = new(); - internal static bool IsDebug(string path) { string assemblyPath = Path.GetFullPath(path); - using MetadataLoadContext loadContext = new(s_resolver); + string? assemblyDirectory = Path.GetDirectoryName(assemblyPath); + Debug.Assert(assemblyDirectory is not null); + + using MetadataLoadContext loadContext = new(new AssemblyResolver(assemblyDirectory)); Assembly assembly = loadContext.LoadFromAssemblyPath(assemblyPath); foreach (CustomAttributeData attribute in assembly.GetCustomAttributesData()) { @@ -56,10 +58,10 @@ internal static bool IsDebug(string path) return false; } - private sealed class RuntimeAssemblyResolver : MetadataAssemblyResolver + private sealed class AssemblyResolver(string assemblyDirectory) : MetadataAssemblyResolver { - private readonly string _runtimeDirectory = Path.GetDirectoryName(typeof(object).Assembly.Location) - ?? throw new InvalidOperationException("The runtime assembly directory is not available."); + private readonly string _assemblyDirectory = assemblyDirectory; + private readonly string _runtimeDirectory = RuntimeEnvironment.GetRuntimeDirectory(); public override Assembly? Resolve(MetadataLoadContext context, AssemblyName assemblyName) { @@ -68,7 +70,13 @@ private sealed class RuntimeAssemblyResolver : MetadataAssemblyResolver return null; } - string assemblyPath = Path.Combine(_runtimeDirectory, name + ".dll"); + string fileName = name + ".dll"; + string assemblyPath = Path.Combine(_assemblyDirectory, fileName); + if (!File.Exists(assemblyPath)) + { + assemblyPath = Path.Combine(_runtimeDirectory, fileName); + } + return File.Exists(assemblyPath) ? context.LoadFromAssemblyPath(assemblyPath) : null; } }