diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
index bcbd2bb6e4..8f6442c3d5 100644
--- a/.github/workflows/main.yaml
+++ b/.github/workflows/main.yaml
@@ -49,6 +49,8 @@ jobs:
run: dotnet build --no-restore --verbosity=normal
- name: Test
run: ./.ci/gha-run-tests.ps1
+ - name: AotTest
+ run: ./projects/AotCompatibility.TestApp/test-aot-compatibility.ps1
build:
name: build/test on ubuntu-latest
diff --git a/RabbitMQDotNetClient.sln b/RabbitMQDotNetClient.sln
index 1b00ba7763..fc552cb7e3 100644
--- a/RabbitMQDotNetClient.sln
+++ b/RabbitMQDotNetClient.sln
@@ -20,6 +20,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestApplications", "TestApp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OAuth2", "projects\TestApplications\OAuth2\OAuth2.csproj", "{07E203AC-9E4B-4BED-9445-E2B45E10E412}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AotCompatibility.TestApp", "projects\AotCompatibility.TestApp\AotCompatibility.TestApp.csproj", "{0B79BD0B-B35D-4626-ABCC-023B6726A531}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -46,6 +48,10 @@ Global
{07E203AC-9E4B-4BED-9445-E2B45E10E412}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07E203AC-9E4B-4BED-9445-E2B45E10E412}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07E203AC-9E4B-4BED-9445-E2B45E10E412}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0B79BD0B-B35D-4626-ABCC-023B6726A531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B79BD0B-B35D-4626-ABCC-023B6726A531}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0B79BD0B-B35D-4626-ABCC-023B6726A531}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0B79BD0B-B35D-4626-ABCC-023B6726A531}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/projects/AotCompatibility.TestApp/AotCompatibility.TestApp.csproj b/projects/AotCompatibility.TestApp/AotCompatibility.TestApp.csproj
new file mode 100644
index 0000000000..5109eeebed
--- /dev/null
+++ b/projects/AotCompatibility.TestApp/AotCompatibility.TestApp.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net6.0
+ enable
+
+ true
+ false
+
+
+
+
+
+
+
+
+
diff --git a/projects/AotCompatibility.TestApp/Program.cs b/projects/AotCompatibility.TestApp/Program.cs
new file mode 100644
index 0000000000..7109a11906
--- /dev/null
+++ b/projects/AotCompatibility.TestApp/Program.cs
@@ -0,0 +1,4 @@
+// publishing this app ensures all of the code in the referenced
+// assemblies are trim/AOT compatible.
+
+Console.WriteLine("Hello, World!");
\ No newline at end of file
diff --git a/projects/AotCompatibility.TestApp/test-aot-compatibility.ps1 b/projects/AotCompatibility.TestApp/test-aot-compatibility.ps1
new file mode 100644
index 0000000000..6ac50fda95
--- /dev/null
+++ b/projects/AotCompatibility.TestApp/test-aot-compatibility.ps1
@@ -0,0 +1,59 @@
+$DebugPreference = "Continue"
+$ErrorActionPreference = 'Stop'
+# Set-PSDebug -Strict -Trace 1
+Set-PSDebug -Off
+Set-StrictMode -Version 'Latest' -ErrorAction 'Stop' -Verbose
+
+New-Variable -Name rootDirectory -Option Constant -Value $PSScriptRoot
+Write-Host "[INFO] rootDirectory: $rootDirectory"
+
+$runtime = $IsWindows ? "win-x64" : ($IsMacOS ? "macos-x64" : "linux-x64")
+$app = $IsWindows ? "./AotCompatibility.TestApp.exe" : "./AotCompatibility.TestApp"
+
+$publishOutput = dotnet publish --runtime=$runtime $rootDirectory/AotCompatibility.TestApp.csproj -nodeReuse:false '/p:UseSharedCompilation=false' '/p:Configuration=Release'
+
+$actualWarningCount = 0
+
+foreach ($line in $($publishOutput -split "`r`n"))
+{
+ if (($line -like "*analysis warning IL*") -or ($line -like "*analysis error IL*"))
+ {
+ Write-Host $line
+ $actualWarningCount += 1
+ }
+}
+
+Write-Host "Actual warning count is:", $actualWarningCount
+$expectedWarningCount = 0
+
+if ($LastExitCode -ne 0)
+{
+ Write-Error -ErrorAction Continue -Message "[ERROR] error while publishing AotCompatibility Test App, LastExitCode is $LastExitCode"
+ Write-Error -ErrorAction Continue -Message $publishOutput
+}
+
+Push-Location "$rootDirectory/bin/Release/net6.0/$runtime"
+try
+{
+ Write-Host "[INFO] executing: $app"
+ $app
+ Write-Host "[INFO] finished executing test app"
+
+ if ($LastExitCode -ne 0)
+ {
+ Write-Error -ErrorAction Continue -Message "[ERROR] there was an error while executing AotCompatibility Test App. LastExitCode is: $LastExitCode"
+ }
+}
+finally
+{
+ Pop-Location
+}
+
+$exitCode = 0
+if ($actualWarningCount -ne $expectedWarningCount)
+{
+ $exitCode = 1
+ Write-Error -ErrorAction Continue -Message "Actual warning count: $actualWarningCount is not as expected, which is: $expectedWarningCount"
+}
+
+Exit $exitCode
diff --git a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
index 7851d56912..eb4266865a 100755
--- a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
+++ b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
@@ -5,8 +5,6 @@
true
$(NoWarn);CS1591
true
- true
- true
RabbitMQ Client Library for .NET
VMware
VMware, Inc. or its affiliates.
diff --git a/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs
index c3fa966519..696ecda39d 100644
--- a/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs
+++ b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs
@@ -31,6 +31,7 @@
using System;
using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Timers;
@@ -54,16 +55,12 @@ public class TimerBasedCredentialRefresherEventSource : EventSource
[Event(2)]
public void Unregistered(string name) => WriteEvent(2, "UnRegistered", name);
[Event(3)]
-#if NET6_0_OR_GREATER
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe")]
-#endif
public void ScheduledTimer(string name, double interval) => WriteEvent(3, "ScheduledTimer", name, interval);
[Event(4)]
public void TriggeredTimer(string name) => WriteEvent(4, "TriggeredTimer", name);
[Event(5)]
-#if NET6_0_OR_GREATER
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe")]
-#endif
public void RefreshedCredentials(string name, bool succesfully) => WriteEvent(5, "RefreshedCredentials", name, succesfully);
[Event(6)]
public void AlreadyRegistered(string name) => WriteEvent(6, "AlreadyRegistered", name);
diff --git a/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs b/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs
index 4481abcca1..38460b46bd 100644
--- a/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs
+++ b/projects/RabbitMQ.Client/client/logging/RabbitMqClientEventSource.cs
@@ -30,6 +30,7 @@
//---------------------------------------------------------------------------
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
namespace RabbitMQ.Client.Logging
@@ -66,19 +67,14 @@ public void Warn(string message)
public void Error(string message, RabbitMqExceptionDetail ex)
{
if (IsEnabled())
- {
-#if NET6_0_OR_GREATER
WriteExceptionEvent(message, ex);
+ }
- [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The properties are preserved with the DynamicallyAccessedMembers attribute.")]
- void WriteExceptionEvent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string message, T ex)
- {
- WriteEvent(3, message, ex);
- }
-#else
- WriteEvent(3, message, ex);
-#endif
- }
+ [NonEvent]
+ [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The properties are preserved with the DynamicallyAccessedMembers attribute.")]
+ private void WriteExceptionEvent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string message, T ex)
+ {
+ WriteEvent(3, message, ex);
}
[NonEvent]
diff --git a/projects/RabbitMQ.Client/util/TrimmingAttributes.cs b/projects/RabbitMQ.Client/util/TrimmingAttributes.cs
new file mode 100644
index 0000000000..a7aaeebc94
--- /dev/null
+++ b/projects/RabbitMQ.Client/util/TrimmingAttributes.cs
@@ -0,0 +1,253 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+// taken from https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#approach-2-define-the-attributes-internally
+
+namespace System.Diagnostics.CodeAnalysis
+{
+#if !NET5_0_OR_GREATER
+ ///
+ /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a
+ /// single code artifact.
+ ///
+ ///
+ /// is different than
+ /// in that it doesn't have a
+ /// . So it is always preserved in the compiled assembly.
+ ///
+ [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
+ internal sealed class UnconditionalSuppressMessageAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the
+ /// class, specifying the category of the tool and the identifier for an analysis rule.
+ ///
+ /// The category for the attribute.
+ /// The identifier of the analysis rule the attribute applies to.
+ public UnconditionalSuppressMessageAttribute(string category, string checkId)
+ {
+ Category = category;
+ CheckId = checkId;
+ }
+
+ ///
+ /// Gets the category identifying the classification of the attribute.
+ ///
+ ///
+ /// The property describes the tool or tool analysis category
+ /// for which a message suppression attribute applies.
+ ///
+ public string Category { get; }
+
+ ///
+ /// Gets the identifier of the analysis tool rule to be suppressed.
+ ///
+ ///
+ /// Concatenated together, the and
+ /// properties form a unique check identifier.
+ ///
+ public string CheckId { get; }
+
+ ///
+ /// Gets or sets the scope of the code that is relevant for the attribute.
+ ///
+ ///
+ /// The Scope property is an optional argument that specifies the metadata scope for which
+ /// the attribute is relevant.
+ ///
+ public string Scope { get; set; }
+
+ ///
+ /// Gets or sets a fully qualified path that represents the target of the attribute.
+ ///
+ ///
+ /// The property is an optional argument identifying the analysis target
+ /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void".
+ /// Because it is fully qualified, it can be long, particularly for targets such as parameters.
+ /// The analysis tool user interface should be capable of automatically formatting the parameter.
+ ///
+ public string Target { get; set; }
+
+ ///
+ /// Gets or sets an optional argument expanding on exclusion criteria.
+ ///
+ ///
+ /// The property is an optional argument that specifies additional
+ /// exclusion where the literal metadata target is not sufficiently precise. For example,
+ /// the cannot be applied within a method,
+ /// and it may be desirable to suppress a violation against a statement in the method that will
+ /// give a rule violation, but not against all statements in the method.
+ ///
+ public string MessageId { get; set; }
+
+ ///
+ /// Gets or sets the justification for suppressing the code analysis message.
+ ///
+ public string Justification { get; set; }
+ }
+
+ ///
+ /// Indicates that certain members on a specified are accessed dynamically,
+ /// for example through .
+ ///
+ ///
+ /// This allows tools to understand which members are being accessed during the execution
+ /// of a program.
+ ///
+ /// This attribute is valid on members whose type is or .
+ ///
+ /// When this attribute is applied to a location of type , the assumption is
+ /// that the string represents a fully qualified type name.
+ ///
+ /// When this attribute is applied to a class, interface, or struct, the members specified
+ /// can be accessed dynamically on instances returned from calling
+ /// on instances of that class, interface, or struct.
+ ///
+ /// If the attribute is applied to a method it's treated as a special case and it implies
+ /// the attribute should be applied to the "this" parameter of the method. As such the attribute
+ /// should only be used on instance methods of types assignable to System.Type (or string, but no methods
+ /// will use it there).
+ ///
+ [AttributeUsage(
+ AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
+ AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method |
+ AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
+ Inherited = false)]
+ internal sealed class DynamicallyAccessedMembersAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified member types.
+ ///
+ /// The types of members dynamically accessed.
+ public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
+ {
+ MemberTypes = memberTypes;
+ }
+
+ ///
+ /// Gets the which specifies the type
+ /// of members dynamically accessed.
+ ///
+ public DynamicallyAccessedMemberTypes MemberTypes { get; }
+ }
+
+ ///
+ /// Specifies the types of members that are dynamically accessed.
+ ///
+ /// This enumeration has a attribute that allows a
+ /// bitwise combination of its member values.
+ ///
+ [Flags]
+ internal enum DynamicallyAccessedMemberTypes
+ {
+ ///
+ /// Specifies no members.
+ ///
+ None = 0,
+
+ ///
+ /// Specifies the default, parameterless public constructor.
+ ///
+ PublicParameterlessConstructor = 0x0001,
+
+ ///
+ /// Specifies all public constructors.
+ ///
+ PublicConstructors = 0x0002 | PublicParameterlessConstructor,
+
+ ///
+ /// Specifies all non-public constructors.
+ ///
+ NonPublicConstructors = 0x0004,
+
+ ///
+ /// Specifies all public methods.
+ ///
+ PublicMethods = 0x0008,
+
+ ///
+ /// Specifies all non-public methods.
+ ///
+ NonPublicMethods = 0x0010,
+
+ ///
+ /// Specifies all public fields.
+ ///
+ PublicFields = 0x0020,
+
+ ///
+ /// Specifies all non-public fields.
+ ///
+ NonPublicFields = 0x0040,
+
+ ///
+ /// Specifies all public nested types.
+ ///
+ PublicNestedTypes = 0x0080,
+
+ ///
+ /// Specifies all non-public nested types.
+ ///
+ NonPublicNestedTypes = 0x0100,
+
+ ///
+ /// Specifies all public properties.
+ ///
+ PublicProperties = 0x0200,
+
+ ///
+ /// Specifies all non-public properties.
+ ///
+ NonPublicProperties = 0x0400,
+
+ ///
+ /// Specifies all public events.
+ ///
+ PublicEvents = 0x0800,
+
+ ///
+ /// Specifies all non-public events.
+ ///
+ NonPublicEvents = 0x1000,
+
+ ///
+ /// Specifies all interfaces implemented by the type.
+ ///
+ Interfaces = 0x2000,
+
+ ///
+ /// Specifies all members.
+ ///
+ All = ~None
+ }
+#endif
+}
diff --git a/projects/RabbitMQ.Client/util/DebugUtil.cs b/projects/Unit/Helper/DebugUtil.cs
similarity index 99%
rename from projects/RabbitMQ.Client/util/DebugUtil.cs
rename to projects/Unit/Helper/DebugUtil.cs
index 3a6d50f57e..28f55fc677 100644
--- a/projects/RabbitMQ.Client/util/DebugUtil.cs
+++ b/projects/Unit/Helper/DebugUtil.cs
@@ -34,7 +34,7 @@
using System.IO;
using System.Reflection;
-namespace RabbitMQ.Util
+namespace RabbitMQ.Client.Unit
{
///Miscellaneous debugging and development utilities.
///
diff --git a/projects/Unit/TestContentHeaderCodec.cs b/projects/Unit/TestContentHeaderCodec.cs
index 7b6585c8e4..3655d5a301 100644
--- a/projects/Unit/TestContentHeaderCodec.cs
+++ b/projects/Unit/TestContentHeaderCodec.cs
@@ -34,7 +34,6 @@
using NUnit.Framework;
using RabbitMQ.Client.Impl;
-using RabbitMQ.Util;
namespace RabbitMQ.Client.Unit
{
diff --git a/projects/Unit/TestMethodArgumentCodec.cs b/projects/Unit/TestMethodArgumentCodec.cs
index a424199b4b..2eca16ecc1 100644
--- a/projects/Unit/TestMethodArgumentCodec.cs
+++ b/projects/Unit/TestMethodArgumentCodec.cs
@@ -36,7 +36,6 @@
using NUnit.Framework;
using RabbitMQ.Client.Impl;
-using RabbitMQ.Util;
namespace RabbitMQ.Client.Unit
{
diff --git a/projects/Unit/WireFormattingFixture.cs b/projects/Unit/WireFormattingFixture.cs
index dda5facae0..c290b7c2d3 100644
--- a/projects/Unit/WireFormattingFixture.cs
+++ b/projects/Unit/WireFormattingFixture.cs
@@ -30,12 +30,9 @@
//---------------------------------------------------------------------------
using System;
-using System.IO;
using NUnit.Framework;
-using RabbitMQ.Util;
-
namespace RabbitMQ.Client.Unit
{
class WireFormattingFixture