FileVersionInfo.GetVersionInfo will report different versions on Windows and Unix.
For example, the published NuGet package System.Runtime.CompilerServices.Unsafe v4.7.1 will report 4.700.20.12001 on Windows and 4.0.0.0 on Unix.
One effect of this is breaking deterministic builds when using a NuGet package where it happens.
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
Tagging subscribers to this area: @tommcdon, @krwq
See info in area-owners.md if you want to be subscribed.
@krwq
I repro this. Here is FileVersionInfo.GetVersionInfo().ToString()
Windows:
File: C:\Users\danmose\AppData\Local\Temp\7zO4D2C1BC9\System.Runtime.CompilerServices.Unsafe.dll
InternalName: System.Runtime.CompilerServices.Unsafe.dll
OriginalFilename: System.Runtime.CompilerServices.Unsafe.dll
FileVersion: 4.700.20.12001
FileDescription: System.Runtime.CompilerServices.Unsafe
Product: Microsoft® .NET Core
ProductVersion: 3.1.3+8a3ffed558ddf943c1efa87d693227722d6af094
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language: Language Neutral
Ubuntu
File: /home/dan/2/x.dll
InternalName: System.Runtime.CompilerServices.Unsafe.dll
OriginalFilename: System.Runtime.CompilerServices.Unsafe.dll
FileVersion: 4.0.0.0
FileDescription: System.Runtime.CompilerServices.Unsafe
Product: Microsoft® .NET Framework
ProductVersion: 4.0.0.0
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language: Language Neutral
Here is ilspy output for it:
[assembly: AssemblyProduct("Microsoft® .NET Framework")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyFileVersion("4.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.0.0")]
[assembly: AssemblyTitle("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyDescription("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: CLSCompliant(false)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("4.0.6.0")]
and filever -v on Windows
--a-- W32i DLL - 4.700.20.12001 shp 16,976 08-17-2020 system.runtime.compilerservices.unsafe.dll
Language 0x0000 (Language Neutral)
CharSet 0x04b0 Unicode
OleSelfRegister Disabled
CompanyName Microsoft Corporation
FileDescription System.Runtime.CompilerServices.Unsafe
InternalName System.Runtime.CompilerServices.Unsafe.dll
OriginalFilenam System.Runtime.CompilerServices.Unsafe.dll
ProductName Microsoft« .NET Core
ProductVersion 3.1.3+8a3ffed558ddf943c1efa87d693227722d6af094
FileVersion 4.700.20.12001
LegalCopyright ⌐ Microsoft Corporation. All rights reserved.
Comments System.Runtime.CompilerServices.Unsafe
OleSelfRegister Disabled
VS_FIXEDFILEINFO:
Signature: feef04bd
Struc Ver: 00010000
FileVer: 000402bc:00142ee1 (4.700:20.12001)
ProdVer: 00030001:00030000 (3.1:3.0)
FlagMask: 0000003f
Flags: 00000000
OS: 00000004 Win32
FileType: 00000002 Dll
SubType: 00000000
FileDate: 00000000:00000000
Looks like on Windows we get it from GetFileVersionInfoEx which reads FILEVERSION of the VERSIONINFO resource. On Unix, we instead read the AssemblyFileVersionAttribute managed assembly attribute.
There is a comment in the Unix implementation that suggests we assumed they have the same value in normal circumstances:
https://github.com/dotnet/runtime/blob/27c682775b7f66c689a161966e99f6473621c531/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Unix.cs#L17-L25
Looking at some random binaries in the 3.1 and 5.0 product, I do see matching versions. So is this a reasonable assumption, that was somehow broken for some assemblies such as this one? @ericstj have you any context here?
Oh, and this code was not changed in a long time so this is not a 5.0 issue.
One effect of this is breaking deterministic builds when using a NuGet package where it happens.
@Thealexbarney could you give more detail on this scenario?
Looking at some random binaries in the 3.1 and 5.0 product, I do see matching versions. So is this a reasonable assumption, that was somehow broken for some assemblies such as this one? @ericstj have you any context here?
Unsafe is built using ILAsm. ILAsm supports setting the PE version resource but only on Windows. A while back we “fixed” this by making the windows build of the dll generate native resources for ILAsm to link in.
The assembly metadata remains hardcoded, however with @Anipik’s recent header generation work this should be easy to fix. I pointed out this same issue in code review for the 6.0 version changes.
@danmosemsft Yeah, I could have given more information there. I was referring to the deps.json build output being different on Linux vs Windows, not the .dll.
However, I just now tested doing deterministic builds with the standard classlib template on SDK 3.1.401, and the output .dll files weren't the same between Windows and Linux. The Linux builds were the same on different Linux machines and distros. I didn't test multiple Windows machines.
I was producing the same binaries on both Linux and Windows in the 2.1-3.0 days, so something must have changed in the build environment or in the compiler since then.
@ericstj what should be the correct FileVersion here ?
File: lib\netstandard2.0-Debug\System.Runtime.CompilerServices.Unsafe.dll
InternalName: System.Runtime.CompilerServices.Unsafe.dll
OriginalFilename: System.Runtime.CompilerServices.Unsafe.dll
FileVersion: 42.42.42.42424
FileDescription: System.Runtime.CompilerServices.Unsafe
Product: Microsoftr .NET
ProductVersion: 5.0.0
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language: Language Neutral
AssemblyFileVersionAttribute should be set from the value of the MSBUILD $(FileVersion) property.
AssemblyInformationalVersionAttribute should be set from the value of the MSBUILD $(InformationalVersion) property.
This is the same as is done by Microsoft.NET.GenerateAssemblyInfo.targets https://github.com/dotnet/sdk/blob/456f8081a9700ad5c27f7ff169fee5dc6ae95e56/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateAssemblyInfo.targets#L85-L90
This one is fixed