Upgrade C# Protobuf projects to .NET 8.0.

This change updates the target frameworks for various C# projects within Protobuf to .NET 8.0, while also maintaining netstandard2.0 for the core library. Support for older frameworks like net45, net50, and netstandard1.1 has been removed.

This updates our project to match our support policy in https://github.com/google/oss-policies-info/blob/main/foundational-dotnet-support-matrix.md which actually dropped support for net45, net50, etc long ago.

PiperOrigin-RevId: 919111969
This commit is contained in:
Sandy Zhang 2026-05-21 10:02:48 -07:00 committed by Copybara-Service
parent fc1cf1cda9
commit 2c15ca43cf
17 changed files with 33 additions and 50 deletions

View file

@ -27,7 +27,7 @@ jobs:
- name: Run tests - name: Run tests
uses: protocolbuffers/protobuf-ci/docker@v5 uses: protocolbuffers/protobuf-ci/docker@v5
with: with:
image: us-docker.pkg.dev/protobuf-build/containers/test/linux/csharp:9.0.0-3.1.415-6.0.100-9fc33a0c378b5affd3c85d3f5ae4f330993048f7 image: us-docker.pkg.dev/protobuf-build/containers/test/linux/csharp:9.0.0-3.1.415-8.0.100-05b57cb3d33f45f689c1dace146b6b9619d78872
credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }} credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
entrypoint: /bin/bash entrypoint: /bin/bash
command: >- command: >-
@ -35,7 +35,7 @@ jobs:
cd csharp && cd csharp &&
dotnet restore src/Google.Protobuf.sln && dotnet restore src/Google.Protobuf.sln &&
dotnet build -c Release src/Google.Protobuf.sln && dotnet build -c Release src/Google.Protobuf.sln &&
dotnet test -c Release -f net6.0 src/Google.Protobuf.Test/Google.Protobuf.Test.csproj" dotnet test -c Release -f net8.0 src/Google.Protobuf.Test/Google.Protobuf.Test.csproj"
- name: Clear bazel between docker instances - name: Clear bazel between docker instances
run: sudo rm -rf _build .repository-cache run: sudo rm -rf _build .repository-cache
@ -43,10 +43,10 @@ jobs:
- name: Run conformance tests - name: Run conformance tests
uses: protocolbuffers/protobuf-ci/bazel-docker@v5 uses: protocolbuffers/protobuf-ci/bazel-docker@v5
with: with:
image: us-docker.pkg.dev/protobuf-build/containers/test/linux/csharp:9.0.0-3.1.415-6.0.100-9fc33a0c378b5affd3c85d3f5ae4f330993048f7 image: us-docker.pkg.dev/protobuf-build/containers/test/linux/csharp:9.0.0-3.1.415-8.0.100-05b57cb3d33f45f689c1dace146b6b9619d78872
credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }} credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
bazel-cache: csharp_linux bazel-cache: csharp_linux
bazel: test //csharp:conformance_test --action_env=DOTNET_CLI_TELEMETRY_OPTOUT=1 --test_env=DOTNET_CLI_HOME=/home/bazel bazel: test //csharp:conformance_test --action_env=PATH --action_env=DOTNET_CLI_TELEMETRY_OPTOUT=1 --test_env=DOTNET_CLI_HOME=/home/bazel
windows: windows:
name: Windows name: Windows
@ -60,7 +60,7 @@ jobs:
- name: Setup dotnet - name: Setup dotnet
uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5.1.0 uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5.1.0
with: with:
dotnet-version: '6.0.x' dotnet-version: '8.0.x'
- name: Run tests - name: Run tests
uses: protocolbuffers/protobuf-ci/bash@v5 uses: protocolbuffers/protobuf-ci/bash@v5
@ -85,13 +85,13 @@ jobs:
# (we want to avoid references to ~/.nuget that won't be available in the subsequent docker run) # (we want to avoid references to ~/.nuget that won't be available in the subsequent docker run)
uses: protocolbuffers/protobuf-ci/docker@v5 uses: protocolbuffers/protobuf-ci/docker@v5
with: with:
image: mcr.microsoft.com/dotnet/sdk:6.0.100-bullseye-slim image: mcr.microsoft.com/dotnet/sdk:8.0.100-bookworm-slim
credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }} credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
command: >- command: >-
/bin/bash -cex ' /bin/bash -cex '
DOTNET_CLI_TELEMETRY_OPTOUT=true DOTNET_CLI_TELEMETRY_OPTOUT=true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
dotnet publish -c Release -f net6.0 /workspace/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj' dotnet publish -c Release -f net8.0 /workspace/csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj'
- name: Use an actual aarch64 docker image to run protobuf C# tests with an emulator - name: Use an actual aarch64 docker image to run protobuf C# tests with an emulator
# "dotnet vstest" allows running tests from a pre-built project. # "dotnet vstest" allows running tests from a pre-built project.
@ -102,11 +102,11 @@ jobs:
# we just run map the user's home to a throwaway temporary directory # we just run map the user's home to a throwaway temporary directory
uses: protocolbuffers/protobuf-ci/docker@v5 uses: protocolbuffers/protobuf-ci/docker@v5
with: with:
image: mcr.microsoft.com/dotnet/sdk:6.0.100-bullseye-slim-arm64v8 image: mcr.microsoft.com/dotnet/sdk:8.0.100-bookworm-slim-arm64v8
skip-staleness-check: true skip-staleness-check: true
credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }} credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
command: >- command: >-
/bin/bash -cex ' /bin/bash -cex '
DOTNET_CLI_TELEMETRY_OPTOUT=true DOTNET_CLI_TELEMETRY_OPTOUT=true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
dotnet vstest /workspace/csharp/src/Google.Protobuf.Test/bin/Release/net6.0/publish/Google.Protobuf.Test.dll' dotnet vstest /workspace/csharp/src/Google.Protobuf.Test/bin/Release/net8.0/publish/Google.Protobuf.Test.dll'

View file

@ -38,7 +38,7 @@ dotnet restore
dotnet build -c %configuration% || goto error dotnet build -c %configuration% || goto error
echo Testing C# echo Testing C#
dotnet test -c %configuration% -f net6.0 Google.Protobuf.Test\Google.Protobuf.Test.csproj || goto error dotnet test -c %configuration% -f net8.0 Google.Protobuf.Test\Google.Protobuf.Test.csproj || goto error
dotnet test -c %configuration% -f net462 Google.Protobuf.Test\Google.Protobuf.Test.csproj || goto error dotnet test -c %configuration% -f net462 Google.Protobuf.Test\Google.Protobuf.Test.csproj || goto error
goto :EOF goto :EOF

View file

@ -103,7 +103,7 @@ inline_sh_test(
pushd `dirname $(location src/Google.Protobuf.sln)`/.. pushd `dirname $(location src/Google.Protobuf.sln)`/..
dotnet restore src/Google.Protobuf.sln dotnet restore src/Google.Protobuf.sln
dotnet build -c Release src/Google.Protobuf.sln dotnet build -c Release src/Google.Protobuf.sln
dotnet test -c Release -f net6.0 src/Google.Protobuf.Test/Google.Protobuf.Test.csproj dotnet test -c Release -f net8.0 src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
popd popd
""", """,
) )

View file

@ -10,8 +10,8 @@ dotnet restore $SRC/Google.Protobuf.sln
dotnet build -c $CONFIG $SRC/Google.Protobuf.sln dotnet build -c $CONFIG $SRC/Google.Protobuf.sln
echo Running tests. echo Running tests.
# Only test net6.0, which uses the .NET runtime. # Only test net8.0, which uses the .NET runtime.
# If we want to test the .NET 4.5 version separately, we could # If we want to test the .NET 4.5 version separately, we could
# run Mono explicitly. However, we don't have any differences between # run Mono explicitly. However, we don't have any differences between
# the .NET 4.5 and netstandard2.1 assemblies. # the .NET 4.5 and netstandard2.1 assemblies.
dotnet test -c $CONFIG -f net6.0 $SRC/Google.Protobuf.Test/Google.Protobuf.Test.csproj dotnet test -c $CONFIG -f net8.0 $SRC/Google.Protobuf.Test/Google.Protobuf.Test.csproj

View file

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>net462;net6.0</TargetFrameworks> <TargetFrameworks>net462;net8.0</TargetFrameworks>
<AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<IsPackable>False</IsPackable> <IsPackable>False</IsPackable>

View file

@ -22,7 +22,7 @@ function run_test() {
dotnet restore src/Google.Protobuf.Test/Google.Protobuf.Test.csproj dotnet restore src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
dotnet build -c Release src/Google.Protobuf/Google.Protobuf.csproj dotnet build -c Release src/Google.Protobuf/Google.Protobuf.csproj
dotnet build -c Release src/Google.Protobuf.Test/Google.Protobuf.Test.csproj dotnet build -c Release src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
dotnet run -c Release -f net6.0 -p src/Google.Protobuf.Test/Google.Protobuf.Test.csproj dotnet run -c Release -f net8.0 -p src/Google.Protobuf.Test/Google.Protobuf.Test.csproj
} }
set -ex set -ex

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<StartupObject>Google.Protobuf.Examples.AddressBook.Program</StartupObject> <StartupObject>Google.Protobuf.Examples.AddressBook.Program</StartupObject>
<IsPackable>False</IsPackable> <IsPackable>False</IsPackable>

View file

@ -58,7 +58,7 @@ genrule(
SRCDIR=$$(dirname $(location :Program.cs)) SRCDIR=$$(dirname $(location :Program.cs))
cp $(location //conformance:conformance_csharp_proto) $$SRCDIR cp $(location //conformance:conformance_csharp_proto) $$SRCDIR
DOTNET_CLI_HOME=/tmp ./$(location :build_conformance_test) DOTNET_CLI_HOME=/tmp ./$(location :build_conformance_test)
cp -r $$SRCDIR/bin/Release/net6.0/* $(RULEDIR) cp -r $$SRCDIR/bin/Release/net8.0/* $(RULEDIR)
""", """,
tools = [":build_conformance_test"], tools = [":build_conformance_test"],
visibility = ["//visibility:private"], visibility = ["//visibility:private"],

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<IsPackable>False</IsPackable> <IsPackable>False</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<IsPackable>False</IsPackable> <IsPackable>False</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -6,7 +6,7 @@
and without the internal visibility from the test project (all of which have caused issues in the past). and without the internal visibility from the test project (all of which have caused issues in the past).
--> -->
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net462;netstandard2.0</TargetFrameworks> <TargetFrameworks>net462;net8.0;netstandard2.0</TargetFrameworks>
<LangVersion>10.0</LangVersion> <LangVersion>10.0</LangVersion>
<AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net462;net6.0</TargetFrameworks> <TargetFrameworks>net462;net8.0</TargetFrameworks>
<AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<IsPackable>False</IsPackable> <IsPackable>False</IsPackable>

View file

@ -37,7 +37,7 @@ namespace Google.Protobuf
var currentAssemblyDir = Path.GetDirectoryName(typeof(RefStructCompatibilityTest).GetTypeInfo().Assembly.Location); var currentAssemblyDir = Path.GetDirectoryName(typeof(RefStructCompatibilityTest).GetTypeInfo().Assembly.Location);
var testProtosProjectDir = Path.GetFullPath(Path.Combine(currentAssemblyDir, "..", "..", "..", "..", "Google.Protobuf.Test.TestProtos")); var testProtosProjectDir = Path.GetFullPath(Path.Combine(currentAssemblyDir, "..", "..", "..", "..", "Google.Protobuf.Test.TestProtos"));
var testProtosOutputDir = (currentAssemblyDir.Contains("bin/Debug/") || currentAssemblyDir.Contains("bin\\Debug\\")) ? "bin\\Debug\\net462" : "bin\\Release\\net462"; var testProtosOutputDir = (currentAssemblyDir.Contains("bin/Debug/") || currentAssemblyDir.Contains("bin\\Debug\\")) ? "bin\\Debug\\netstandard2.0" : "bin\\Release\\netstandard2.0";
// If "ref struct" types are used in the generated code, compilation with an old compiler will fail with the following error: // If "ref struct" types are used in the generated code, compilation with an old compiler will fail with the following error:
// "XYZ is obsolete: 'Types with embedded references are not supported in this version of your compiler.'" // "XYZ is obsolete: 'Types with embedded references are not supported in this version of your compiler.'"
@ -47,7 +47,7 @@ namespace Google.Protobuf
// We suppress CS1691, which flags a warning for the generated line of // We suppress CS1691, which flags a warning for the generated line of
// #pragma warning disable 1591, 0612, 3021, 8981 // #pragma warning disable 1591, 0612, 3021, 8981
// because CS8981 is unknown to this version of the compiler. // because CS8981 is unknown to this version of the compiler.
var args = $"-langversion:3 -nologo -nowarn:1691 -target:library {compatibilityFlag} -reference:{testProtosOutputDir}\\Google.Protobuf.dll -out:{testProtosOutputDir}\\TestProtos.RefStructCompatibilityTest.OldCompiler.dll {sources}"; var args = $"-langversion:3 -nologo -nowarn:1691 -target:library {compatibilityFlag} -reference:netstandard.dll -reference:{testProtosOutputDir}\\Google.Protobuf.dll -out:{testProtosOutputDir}\\TestProtos.RefStructCompatibilityTest.OldCompiler.dll {sources}";
RunOldCsharpCompilerAndCheckSuccess(args, testProtosProjectDir); RunOldCsharpCompilerAndCheckSuccess(args, testProtosProjectDir);
} }

View file

@ -712,11 +712,7 @@ namespace Google.Protobuf.Collections
// 2. validate that size of csharp element T is matching the size of protobuf wire size // 2. validate that size of csharp element T is matching the size of protobuf wire size
// NOTE: cannot use bool with this span because csharp marshal it as 4 bytes // NOTE: cannot use bool with this span because csharp marshal it as 4 bytes
if (BitConverter.IsLittleEndian && (codec.FixedSize > 0 && if (BitConverter.IsLittleEndian && (codec.FixedSize > 0 &&
#if GOOGLE_PROTOBUF_SUPPORT_GENERIC_SIZEOF
Marshal.SizeOf<T>() Marshal.SizeOf<T>()
#else
Marshal.SizeOf(typeof(T))
#endif
== codec.FixedSize)) == codec.FixedSize))
{ {
handle = GCHandle.Alloc(array, GCHandleType.Pinned); handle = GCHandle.Alloc(array, GCHandleType.Pinned);

View file

@ -8,12 +8,12 @@
<VersionPrefix>3.36.0</VersionPrefix> <VersionPrefix>3.36.0</VersionPrefix>
<LangVersion>10.0</LangVersion> <LangVersion>10.0</LangVersion>
<Authors>Google Inc.</Authors> <Authors>Google Inc.</Authors>
<TargetFrameworks>netstandard1.1;netstandard2.0;net45;net50</TargetFrameworks> <TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../../keys/Google.Protobuf.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<PackageTags>Protocol;Buffers;Binary;Serialization;Format;Google;proto;proto3</PackageTags> <PackageTags>Protocol;Buffers;Binary;Serialization;Format;Google;proto;proto3</PackageTags>
<PackageReleaseNotes>C# proto3 support</PackageReleaseNotes> <PackageReleaseNotes>C# proto support</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/protocolbuffers/protobuf</PackageProjectUrl> <PackageProjectUrl>https://github.com/protocolbuffers/protobuf</PackageProjectUrl>
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression> <PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
@ -28,12 +28,9 @@
<NoWarn>$(NoWarn);NETSDK1195;NETSDK1210</NoWarn> <NoWarn>$(NoWarn);NETSDK1195;NETSDK1210</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);GOOGLE_PROTOBUF_SUPPORT_FAST_STRING;GOOGLE_PROTOBUF_SUPPORT_GENERIC_SIZEOF</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net50' "> <PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<DefineConstants>$(DefineConstants);GOOGLE_PROTOBUF_SUPPORT_FAST_STRING;GOOGLE_PROTOBUF_SUPPORT_GENERIC_SIZEOF;GOOGLE_PROTOBUF_SIMD</DefineConstants> <DefineConstants>$(DefineConstants);GOOGLE_PROTOBUF_SIMD</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -42,14 +39,8 @@
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0"/> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0"/>
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' OR '$(TargetFramework)' == 'netstandard1.1' ">
<!-- TODO -->
<PackageReference Include="System.Memory" Version="4.5.3"/>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Memory" Version="4.5.3"/> <PackageReference Include="System.Memory" Version="4.5.3"/>
<!-- Needed for netcoreapp3.1 to work correctly. .NET is not able to load the assembly without this -->
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2"/> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2"/>
</ItemGroup> </ItemGroup>

View file

@ -570,7 +570,6 @@ namespace Google.Protobuf
throw InvalidProtocolBufferException.NegativeSize(); throw InvalidProtocolBufferException.NegativeSize();
} }
#if GOOGLE_PROTOBUF_SUPPORT_FAST_STRING
if (length <= state.bufferSize - state.bufferPos) if (length <= state.bufferSize - state.bufferPos)
{ {
// Fast path: all bytes to decode appear in the same span. // Fast path: all bytes to decode appear in the same span.
@ -595,7 +594,6 @@ namespace Google.Protobuf
state.bufferPos += length; state.bufferPos += length;
return value; return value;
} }
#endif
return ReadStringSlow(ref buffer, ref state, length); return ReadStringSlow(ref buffer, ref state, length);
} }
@ -607,7 +605,6 @@ namespace Google.Protobuf
{ {
ValidateCurrentLimit(ref buffer, ref state, length); ValidateCurrentLimit(ref buffer, ref state, length);
#if GOOGLE_PROTOBUF_SUPPORT_FAST_STRING
if (IsDataAvailable(ref state, length)) if (IsDataAvailable(ref state, length))
{ {
// Read string data into a temporary buffer, either stackalloc'ed or from ArrayPool // Read string data into a temporary buffer, either stackalloc'ed or from ArrayPool
@ -649,7 +646,6 @@ namespace Google.Protobuf
} }
} }
} }
#endif
// Slow path: Build a byte array first then copy it. // Slow path: Build a byte array first then copy it.
// This will be called when reading from a Stream because we don't know the length of the stream, // This will be called when reading from a Stream because we don't know the length of the stream,

View file

@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"version": "6.0.100", "version": "8.0.100",
"rollForward": "latestMinor" "rollForward": "latestMinor"
} }
} }