/* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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
*
* http://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.
*/
// Adapted from BenchmarkDotNet source https://github.com/dotnet/BenchmarkDotNet/blob/master/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs
#region BenchmarkDotNet License https://github.com/dotnet/BenchmarkDotNet/blob/master/LICENSE.md
// The MIT License
// Copyright (c) 2013–2020.NET Foundation and contributors
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace OpenSearch.Net
{
///
/// Represents the current .NET Runtime version.
///
internal sealed class RuntimeVersionInfo : VersionInfo
{
public static readonly RuntimeVersionInfo Default = new RuntimeVersionInfo { Version = new Version(0, 0, 0), IsPrerelease = false };
public RuntimeVersionInfo() => StoreVersion(GetRuntimeVersion());
private static string GetRuntimeVersion() =>
GetNetCoreVersion();
private static string GetNetCoreVersion()
{
// for .NET 5+ we can use Environment.Version
if (Environment.Version.Major >= 5)
{
const string dotNet = ".NET ";
var index = RuntimeInformation.FrameworkDescription.IndexOf(dotNet, StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
return RuntimeInformation.FrameworkDescription.Substring(dotNet.Length);
}
}
// next, try using file version info
var systemPrivateCoreLib = FileVersionInfo.GetVersionInfo(typeof(object).Assembly.Location);
if (TryGetVersionFromProductInfo(systemPrivateCoreLib.ProductVersion, systemPrivateCoreLib.ProductName, out var runtimeVersion))
{
return runtimeVersion;
}
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
if (TryGetVersionFromAssemblyPath(assembly, out runtimeVersion))
{
return runtimeVersion;
}
//At this point, we can't identify whether this is a prerelease, but a version is better than nothing!
var frameworkName = Assembly.GetEntryAssembly()?.GetCustomAttribute()?.FrameworkName;
if (TryGetVersionFromFrameworkName(frameworkName, out runtimeVersion))
{
return runtimeVersion;
}
if (IsRunningInContainer)
{
var dotNetVersion = Environment.GetEnvironmentVariable("DOTNET_VERSION");
var aspNetCoreVersion = Environment.GetEnvironmentVariable("ASPNETCORE_VERSION");
return dotNetVersion ?? aspNetCoreVersion;
}
return null;
}
private static bool TryGetVersionFromAssemblyPath(Assembly assembly, out string runtimeVersion)
{
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
var netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
{
runtimeVersion = assemblyPath[netCoreAppIndex + 1];
return true;
}
runtimeVersion = null;
return false;
}
// NOTE: 5.0.1 FrameworkDescription returns .NET 5.0.1-servicing.20575.16, so we special case servicing as NOT prerelease
protected override bool ContainsPrerelease(string version) => base.ContainsPrerelease(version) && !version.Contains("-servicing");
// sample input:
// 2.0: 4.6.26614.01 @BuiltBy: dlab14-DDVSOWINAGE018 @Commit: a536e7eec55c538c94639cefe295aa672996bf9b, Microsoft .NET Framework
// 2.1: 4.6.27817.01 @BuiltBy: dlab14-DDVSOWINAGE101 @Branch: release/2.1 @SrcCode: https://github.com/dotnet/coreclr/tree/6f78fbb3f964b4f407a2efb713a186384a167e5c, Microsoft .NET Framework
// 2.2: 4.6.27817.03 @BuiltBy: dlab14-DDVSOWINAGE101 @Branch: release/2.2 @SrcCode: https://github.com/dotnet/coreclr/tree/ce1d090d33b400a25620c0145046471495067cc7, Microsoft .NET Framework
// 3.0: 3.0.0-preview8.19379.2+ac25be694a5385a6a1496db40de932df0689b742, Microsoft .NET Core
// 5.0: 5.0.0-alpha1.19413.7+0ecefa44c9d66adb8a997d5778dc6c246ad393a7, Microsoft .NET Core
private static bool TryGetVersionFromProductInfo(string productVersion, string productName, out string version)
{
if (string.IsNullOrEmpty(productVersion) || string.IsNullOrEmpty(productName))
{
version = null;
return false;
}
// yes, .NET Core 2.X has a product name == .NET Framework...
if (productName.IndexOf(".NET Framework", StringComparison.OrdinalIgnoreCase) >= 0)
{
const string releaseVersionPrefix = "release/";
var releaseVersionIndex = productVersion.IndexOf(releaseVersionPrefix);
if (releaseVersionIndex > 0)
{
version = productVersion.Substring(releaseVersionIndex + releaseVersionPrefix.Length);
return true;
}
}
// matches .NET Core and also .NET 5+
if (productName.IndexOf(".NET", StringComparison.OrdinalIgnoreCase) >= 0)
{
version = productVersion;
return true;
}
version = null;
return false;
}
// sample input:
// .NETCoreApp,Version=v2.0
// .NETCoreApp,Version=v2.1
private static bool TryGetVersionFromFrameworkName(string frameworkName, out string runtimeVersion)
{
const string versionPrefix = ".NETCoreApp,Version=v";
if (!string.IsNullOrEmpty(frameworkName) && frameworkName.StartsWith(versionPrefix))
{
runtimeVersion = frameworkName.Substring(versionPrefix.Length);
return true;
}
runtimeVersion = null;
return false;
}
private static bool IsRunningInContainer => string.Equals(Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER"), "true");
}
}