Explorar o código

Add .NET 9 target (#1480)

* Add .NET 9 target

* Disable SonarSource S3236

This following change in the runtime now causes this analyzer
to complain about some Debug.Assert calls which doesn't make sense.

https://github.com/dotnet/core/blob/main/release-notes/9.0/preview/preview7/libraries.md#debugassert-now-reports-assert-condition-by-default
https://rules.sonarsource.com/csharp/RSPEC-3236/

* make use of .NET 9 Lock type

see https://github.com/dotnet/runtime/issues/34812

* Define own Lock type to avoid ifdefs

* revert irrelevant style changes

* update global.json

* Keep net8.0 target in IntegrationTests

Co-authored-by: Rob Hague <rob.hague00@gmail.com>

* fix Package Downgrade Warning

for some reason this happens starting with .NET 9.0 RC2:

/home/mus/git/SSH.NET/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj : error NU1605:
      Warning As Error: Detected package downgrade: BouncyCastle.Cryptography from 2.4.0 to 2.3.1. Reference the package directly from the project to select a different version.
       Renci.SshNet.IntegrationTests -> SSH.NET 1.0.0 -> BouncyCastle.Cryptography (>= 2.4.0)
       Renci.SshNet.IntegrationTests -> Testcontainers 3.10.0 -> BouncyCastle.Cryptography (>= 2.3.1)

* update global.json to RC2

* update global.json to .NET 9 GA

* update GitHub Actions for .NET 9

---------

Co-authored-by: Rob Hague <rob.hague00@gmail.com>
mus65 hai 11 meses
pai
achega
752b1db8f0

+ 3 - 0
.editorconfig

@@ -195,6 +195,9 @@ dotnet_diagnostic.S2971.severity = none
 # This is rather harmless.
 dotnet_diagnostic.S3218.severity = none
 
+# S3236: Remove this argument from the method call; it hides the caller information.
+dotnet_diagnostic.S3236.severity = none
+
 # S3267: Loops should be simplified with "LINQ" expressions
 # https://rules.sonarsource.com/csharp/RSPEC-3267
 #

+ 9 - 9
.github/workflows/build.yml

@@ -17,13 +17,13 @@ jobs:
     - name: Setup .NET
       uses: actions/setup-dotnet@v4
       with:
-        dotnet-version: 8.0.x
+        dotnet-version: 9.0.x
 
     - name: Build Unit Tests .NET
-      run: dotnet build -f net8.0 test/Renci.SshNet.Tests/
+      run: dotnet build -f net9.0 test/Renci.SshNet.Tests/
 
     - name: Build IntegrationTests .NET
-      run: dotnet build -f net8.0 test/Renci.SshNet.IntegrationTests/
+      run: dotnet build -f net9.0 test/Renci.SshNet.IntegrationTests/
 
     - name: Build IntegrationTests .NET Framework
       run:  dotnet build -f net48 test/Renci.SshNet.IntegrationTests/
@@ -31,25 +31,25 @@ jobs:
     - name: Run Unit Tests .NET
       run: |
         dotnet test \
-          -f net8.0 \
+          -f net9.0 \
           --no-build \
           --logger "console;verbosity=normal" \
           --logger GitHubActions \
           -p:CollectCoverage=true \
           -p:CoverletOutputFormat=cobertura \
-          -p:CoverletOutput=../../coverlet/linux_unit_test_net_8_coverage.xml \
+          -p:CoverletOutput=../../coverlet/linux_unit_test_net_9_coverage.xml \
           test/Renci.SshNet.Tests/
 
     - name: Run Integration Tests .NET
       run: |
         dotnet test \
-          -f net8.0 \
+          -f net9.0 \
           --no-build \
           --logger "console;verbosity=normal" \
           --logger GitHubActions \
           -p:CollectCoverage=true \
           -p:CoverletOutputFormat=cobertura \
-          -p:CoverletOutput=../../coverlet/linux_integration_test_net_8_coverage.xml \
+          -p:CoverletOutput=../../coverlet/linux_integration_test_net_9_coverage.xml \
           test/Renci.SshNet.IntegrationTests/
 
     # Also run a subset of the integration tests targeting netfx using mono. This is a temporary measure to get
@@ -111,13 +111,13 @@ jobs:
     - name: Run Unit Tests .NET
       run: |
         dotnet test `
-          -f net8.0 `
+          -f net9.0 `
           --no-build `
           --logger "console;verbosity=normal" `
           --logger GitHubActions `
           -p:CollectCoverage=true `
           -p:CoverletOutputFormat=cobertura `
-          -p:CoverletOutput=../../coverlet/windows_unit_test_net_8_coverage.xml `
+          -p:CoverletOutput=../../coverlet/windows_unit_test_net_9_coverage.xml `
           test/Renci.SshNet.Tests/
 
     - name: Run Unit Tests .NET Framework

+ 1 - 1
global.json

@@ -1,6 +1,6 @@
 {
   "sdk": {
-    "version": "8.0.100",
+    "version": "9.0.100",
     "rollForward": "latestMajor"
   }
 }

+ 2 - 2
src/Renci.SshNet/Channels/Channel.cs

@@ -14,8 +14,8 @@ namespace Renci.SshNet.Channels
     /// </summary>
     internal abstract class Channel : IChannel
     {
-        private readonly object _serverWindowSizeLock = new object();
-        private readonly object _messagingLock = new object();
+        private readonly Lock _serverWindowSizeLock = new Lock();
+        private readonly Lock _messagingLock = new Lock();
         private readonly uint _initialWindowSize;
         private readonly ISession _session;
         private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(initialState: false);

+ 1 - 1
src/Renci.SshNet/Channels/ChannelDirectTcpip.cs

@@ -14,7 +14,7 @@ namespace Renci.SshNet.Channels
     /// </summary>
     internal sealed class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip
     {
-        private readonly object _socketLock = new object();
+        private readonly Lock _socketLock = new Lock();
 
         private EventWaitHandle _channelOpen = new AutoResetEvent(initialState: false);
         private EventWaitHandle _channelData = new AutoResetEvent(initialState: false);

+ 4 - 1
src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs

@@ -1,6 +1,9 @@
 using System;
 using System.Net;
 using System.Net.Sockets;
+#if NET9_0_OR_GREATER
+using System.Threading;
+#endif
 
 using Renci.SshNet.Abstractions;
 using Renci.SshNet.Common;
@@ -13,7 +16,7 @@ namespace Renci.SshNet.Channels
     /// </summary>
     internal sealed class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip
     {
-        private readonly object _socketShutdownAndCloseLock = new object();
+        private readonly Lock _socketShutdownAndCloseLock = new Lock();
         private Socket _socket;
         private IForwardedPort _forwardedPort;
 

+ 19 - 0
src/Renci.SshNet/Common/Lock.cs

@@ -0,0 +1,19 @@
+#if !NET9_0_OR_GREATER
+using System.Threading;
+
+namespace Renci.SshNet.Common
+{
+    internal sealed class Lock
+    {
+        public bool TryEnter()
+        {
+            return Monitor.TryEnter(this);
+        }
+
+        public void Exit()
+        {
+            Monitor.Exit(this);
+        }
+    }
+}
+#endif

+ 1 - 1
src/Renci.SshNet/Renci.SshNet.csproj

@@ -4,7 +4,7 @@
     <AssemblyName>Renci.SshNet</AssemblyName>
     <Product>SSH.NET</Product>
     <AssemblyTitle>SSH.NET</AssemblyTitle>
-    <TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
   </PropertyGroup>
 
   <PropertyGroup>

+ 4 - 4
src/Renci.SshNet/Session.cs

@@ -80,7 +80,7 @@ namespace Renci.SshNet
         /// Holds an object that is used to ensure only a single thread can read from
         /// <see cref="_socket"/> at any given time.
         /// </summary>
-        private readonly object _socketReadLock = new object();
+        private readonly Lock _socketReadLock = new Lock();
 
         /// <summary>
         /// Holds an object that is used to ensure only a single thread can write to
@@ -90,7 +90,7 @@ namespace Renci.SshNet
         /// This is also used to ensure that <see cref="_outboundPacketSequence"/> is
         /// incremented atomatically.
         /// </remarks>
-        private readonly object _socketWriteLock = new object();
+        private readonly Lock _socketWriteLock = new Lock();
 
         /// <summary>
         /// Holds an object that is used to ensure only a single thread can dispose
@@ -1894,7 +1894,7 @@ namespace Renci.SshNet
                     return false;
                 }
 
-                if (!Monitor.TryEnter(_socketReadLock))
+                if (!_socketReadLock.TryEnter())
                 {
                     return true;
                 }
@@ -1906,7 +1906,7 @@ namespace Renci.SshNet
                 }
                 finally
                 {
-                    Monitor.Exit(_socketReadLock);
+                    _socketReadLock.Exit();
                 }
             }
             finally

+ 1 - 1
src/Renci.SshNet/Sftp/SftpFileStream.cs

@@ -18,7 +18,7 @@ namespace Renci.SshNet.Sftp
 #pragma warning restore IDE0079
     public class SftpFileStream : Stream
     {
-        private readonly object _lock = new object();
+        private readonly Lock _lock = new Lock();
         private readonly int _readBufferSize;
         private readonly int _writeBufferSize;
 

+ 4 - 1
src/Renci.SshNet/SshMessageFactory.cs

@@ -1,6 +1,9 @@
 using System;
 using System.Collections.Generic;
 using System.Globalization;
+#if NET9_0_OR_GREATER
+using System.Threading;
+#endif
 
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages;
@@ -14,7 +17,7 @@ namespace Renci.SshNet
     {
         private readonly MessageMetadata[] _enabledMessagesByNumber;
         private readonly bool[] _activatedMessagesById;
-        private readonly object _lock = new object();
+        private readonly Lock _lock = new Lock();
 
         internal static readonly MessageMetadata[] AllMessages = new MessageMetadata[]
             {

+ 1 - 1
test/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
   </PropertyGroup>

+ 1 - 1
test/Renci.SshNet.IntegrationBenchmarks/Renci.SshNet.IntegrationBenchmarks.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
   </PropertyGroup>

+ 1 - 1
test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net48;net8.0</TargetFrameworks>
+    <TargetFrameworks>net48;net8.0;net9.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <IsTestProject>true</IsTestProject>
     <NoWarn>$(NoWarn);SYSLIB0021;SYSLIB1045;SYSLIB0014;IDE0220;IDE0010</NoWarn>

+ 4 - 2
test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs

@@ -4,6 +4,8 @@ using System.Collections.Generic;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
+
+using Renci.SshNet.Common;
 #pragma warning restore IDE0005
 
 namespace Renci.SshNet.Tests.Common
@@ -13,7 +15,7 @@ namespace Renci.SshNet.Tests.Common
         private readonly IPEndPoint _endPoint;
         private readonly ManualResetEvent _acceptCallbackDone;
         private readonly List<Socket> _connectedClients;
-        private readonly object _syncLock;
+        private readonly Lock _syncLock;
         private Socket _listener;
         private Thread _receiveThread;
         private bool _started;
@@ -31,7 +33,7 @@ namespace Renci.SshNet.Tests.Common
             _endPoint = endPoint;
             _acceptCallbackDone = new ManualResetEvent(false);
             _connectedClients = new List<Socket>();
-            _syncLock = new object();
+            _syncLock = new Lock();
             ShutdownRemoteCommunicationSocket = true;
         }
 

+ 1 - 1
test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;net6.0;net7.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net462;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
   </PropertyGroup>
 
   <ItemGroup>