Skip to content

Commit 4f646d3

Browse files
authored
Fix IsNetCore and IsNativeAOT for single-file apps without AOT (#2799)
* Fix `IsNetCore` check for single-file apps without AOT, and `IsNativeAOT` check. * Handle `TryGetVersion` for single-file exe in netcoreapp3.x. * Simplify some checks. * Get version from FrameworkDescription. * Don't include confusing FrameworkDescription in netcoreapp2.x. * Rename GetVersionFromFrameworkDescription.
1 parent ab703aa commit 4f646d3

File tree

2 files changed

+52
-39
lines changed

2 files changed

+52
-39
lines changed

src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,26 @@ internal static bool TryGetVersion(out Version? version)
9999
return true;
100100
}
101101

102-
var systemPrivateCoreLib = FileVersionInfo.GetVersionInfo(typeof(object).Assembly.Location);
103-
// systemPrivateCoreLib.Product*Part properties return 0 so we have to implement some ugly parsing...
104-
if (TryGetVersionFromProductInfo(systemPrivateCoreLib.ProductVersion, systemPrivateCoreLib.ProductName, out version))
102+
string coreclrLocation = typeof(object).Assembly.Location;
103+
// Single-file publish has empty assembly location.
104+
if (!string.IsNullOrEmpty(coreclrLocation))
105105
{
106-
return true;
106+
var systemPrivateCoreLib = FileVersionInfo.GetVersionInfo(coreclrLocation);
107+
// systemPrivateCoreLib.Product*Part properties return 0 so we have to implement some ugly parsing...
108+
if (TryGetVersionFromProductInfo(systemPrivateCoreLib.ProductVersion, systemPrivateCoreLib.ProductName, out version))
109+
{
110+
return true;
111+
}
112+
}
113+
else
114+
{
115+
// .Net Core 3.X supports single-file publish, .Net Core 2.X does not.
116+
// .Net Core 3.X fixed the version in FrameworkDescription, so we don't need to handle the case of 4.6.x in this branch.
117+
var frameworkDescriptionVersion = GetParsableVersionPart(GetVersionFromFrameworkDescription());
118+
if (Version.TryParse(frameworkDescriptionVersion, out version))
119+
{
120+
return true;
121+
}
107122
}
108123

109124
// it's OK to use this method only after checking the previous ones
@@ -125,6 +140,14 @@ internal static bool TryGetVersion(out Version? version)
125140
return false;
126141
}
127142

143+
internal static string GetVersionFromFrameworkDescription()
144+
{
145+
// .NET 10.0.0-preview.5.25277.114 -> 10.0.0-preview.5.25277.114
146+
// .NET Core 3.1.32 -> 3.1.32
147+
string frameworkDescription = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
148+
return new string(frameworkDescription.SkipWhile(c => !char.IsDigit(c)).ToArray());
149+
}
150+
128151
// sample input:
129152
// for dotnet run: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.12\
130153
// for dotnet publish: C:\Users\adsitnik\source\repos\ConsoleApp25\ConsoleApp25\bin\Release\netcoreapp2.0\win-x64\publish\

src/BenchmarkDotNet/Portability/RuntimeInformation.cs

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,16 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.Diagnostics.CodeAnalysis;
5-
using System.IO;
65
using System.Linq;
76
using System.Management;
87
using System.Reflection;
98
using System.Runtime.InteropServices;
109
using System.Text.RegularExpressions;
1110
using BenchmarkDotNet.Detectors;
12-
using BenchmarkDotNet.Detectors.Cpu;
1311
using BenchmarkDotNet.Environments;
1412
using BenchmarkDotNet.Extensions;
1513
using BenchmarkDotNet.Helpers;
16-
using JetBrains.Annotations;
17-
using Microsoft.Win32;
18-
using Perfolizer.Helpers;
1914
using static System.Runtime.InteropServices.RuntimeInformation;
20-
using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
2115

2216
namespace BenchmarkDotNet.Portability
2317
{
@@ -48,26 +42,15 @@ internal static class RuntimeInformation
4842
FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase);
4943
#endif
5044

51-
public static readonly bool IsNetNative = FrameworkDescription.StartsWith(".NET Native", StringComparison.OrdinalIgnoreCase);
52-
53-
public static readonly bool IsNetCore =
54-
((Environment.Version.Major >= 5) || FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase))
55-
&& !string.IsNullOrEmpty(typeof(object).Assembly.Location);
56-
5745
#if NET6_0_OR_GREATER
5846
[System.Runtime.Versioning.SupportedOSPlatformGuard("browser")]
5947
public static readonly bool IsWasm = OperatingSystem.IsBrowser();
6048
#else
6149
public static readonly bool IsWasm = IsOSPlatform(OSPlatform.Create("BROWSER"));
6250
#endif
6351

64-
public static readonly bool IsNativeAOT =
65-
Environment.Version.Major >= 5
66-
&& string.IsNullOrEmpty(typeof(object).Assembly.Location) // it's merged to a single .exe and .Location returns null
67-
&& !IsWasm; // Wasm also returns "" for assembly locations
68-
6952
#if NETSTANDARD2_0
70-
public static readonly bool IsAot = IsAotMethod();
53+
public static readonly bool IsAot = IsAotMethod() || FrameworkDescription.StartsWith(".NET Native", StringComparison.OrdinalIgnoreCase);
7154

7255
private static bool IsAotMethod()
7356
{
@@ -88,6 +71,16 @@ private static bool IsAotMethod()
8871
public static readonly bool IsAot = !System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeCompiled;
8972
#endif
9073

74+
public static bool IsNetCore
75+
=> ((Environment.Version.Major >= 5) || FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase))
76+
&& !IsAot;
77+
78+
public static bool IsNativeAOT
79+
=> Environment.Version.Major >= 5
80+
&& IsAot
81+
&& !IsWasm && !IsMono; // Wasm and MonoAOTLLVM are also AOT
82+
83+
9184
public static readonly bool IsTieredJitEnabled =
9285
IsNetCore
9386
&& (Environment.Version.Major < 3
@@ -171,24 +164,21 @@ private static string GetNetCoreVersion()
171164
{
172165
return $".NET {Environment.Version}";
173166
}
174-
else
175-
{
176-
var coreclrAssemblyInfo = FileVersionInfo.GetVersionInfo(typeof(object).GetTypeInfo().Assembly.Location);
177-
var corefxAssemblyInfo = FileVersionInfo.GetVersionInfo(typeof(Regex).GetTypeInfo().Assembly.Location);
178167

179-
if (CoreRuntime.TryGetVersion(out var version) && version >= new Version(5, 0))
180-
{
181-
// after the merge of dotnet/corefx and dotnet/coreclr into dotnet/runtime the version should always be the same
182-
Debug.Assert(coreclrAssemblyInfo.FileVersion == corefxAssemblyInfo.FileVersion);
183-
184-
return $".NET {version} ({coreclrAssemblyInfo.FileVersion})";
185-
}
186-
else
187-
{
188-
string runtimeVersion = version != default ? version.ToString() : Unknown;
168+
return CoreRuntime.TryGetVersion(out var version) && version.Major >= 5
169+
? $".NET {version} ({GetDetailedVersion()})"
170+
: $".NET Core {version?.ToString() ?? Unknown} ({GetDetailedVersion()})";
189171

190-
return $".NET Core {runtimeVersion} (CoreCLR {coreclrAssemblyInfo.FileVersion}, CoreFX {corefxAssemblyInfo.FileVersion})";
191-
}
172+
string GetDetailedVersion()
173+
{
174+
string coreclrLocation = typeof(object).GetTypeInfo().Assembly.Location;
175+
// Single-file publish has empty assembly location.
176+
if (string.IsNullOrEmpty(coreclrLocation))
177+
return CoreRuntime.GetVersionFromFrameworkDescription();
178+
// .Net Core 2.X has confusing FrameworkDescription like 4.6.X.
179+
if (version?.Major >= 3)
180+
return $"{CoreRuntime.GetVersionFromFrameworkDescription()}, {FileVersionInfo.GetVersionInfo(coreclrLocation).FileVersion}";
181+
return FileVersionInfo.GetVersionInfo(coreclrLocation).FileVersion;
192182
}
193183
}
194184

@@ -271,7 +261,7 @@ internal static string GetJitInfo()
271261
{
272262
if (IsNativeAOT)
273263
return "NativeAOT";
274-
if (IsNetNative || IsAot)
264+
if (IsAot)
275265
return "AOT";
276266
if (IsMono || IsWasm)
277267
return ""; // There is no helpful information about JIT on Mono

0 commit comments

Comments
 (0)