File: Shared\TargetFrameworkHelper.cs
Web Access
Project: ..\..\..\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
 
namespace Microsoft.Tools.ServiceModel.Svcutil
{
    internal class TargetFrameworkHelper
    {
        internal const string MinSupportedDotNetVersion = "8.0";
        public static Version MinSupportedNetFxVersionForDotNet { get; } = new Version("4.5");
        public static Version MinSupportedNetStandardVersion { get; } = new Version("1.3");
        public static Version MinSupportedNetCoreAppVersion { get; } = new Version("1.0");
 
        internal static SortedDictionary<string, List<ProjectDependency>> NetCoreVersionReferenceTable = new SortedDictionary<string, List<ProjectDependency>>
        {
            {"Legacy", new List<ProjectDependency> {
                ProjectDependency.FromPackage("System.ServiceModel.Duplex", "4.10.*"  ),
                ProjectDependency.FromPackage("System.ServiceModel.Http", "4.10.*"    ),
                ProjectDependency.FromPackage("System.ServiceModel.NetTcp", "4.10.*"  ),
                ProjectDependency.FromPackage("System.ServiceModel.Security", "4.10.*"),
                ProjectDependency.FromPackage("System.ServiceModel.Federation", "4.10.*")
            } },
            {"Net8", new List<ProjectDependency> {
                ProjectDependency.FromPackage("System.ServiceModel.Http", "8.*"),
                ProjectDependency.FromPackage("System.ServiceModel.NetTcp", "8.*"),
                ProjectDependency.FromPackage("System.ServiceModel.Primitives", "8.*")
            } },
            {"Net10", new List<ProjectDependency> {
                ProjectDependency.FromPackage("System.ServiceModel.Http", "10.*"),
                ProjectDependency.FromPackage("System.ServiceModel.NetTcp", "10.*"),
                ProjectDependency.FromPackage("System.ServiceModel.Primitives", "10.*")
            } }
        };
 
        internal static List<ProjectDependency> FullFrameworkReferences = new List<ProjectDependency>()
        {
            ProjectDependency.FromAssembly("System.ServiceModel"),
        };
 
        internal static List<ProjectDependency> ServiceModelPackages = new List<ProjectDependency>()
        {
            ProjectDependency.FromPackage("System.ServiceModel.Duplex", "*"),
            ProjectDependency.FromPackage("System.ServiceModel.Http", "*" ),
            ProjectDependency.FromPackage("System.ServiceModel.NetTcp", "*"),
            ProjectDependency.FromPackage("System.ServiceModel.Primitives", "*"),
            ProjectDependency.FromPackage("System.ServiceModel", "System.ServiceModel.Primitives", "*"),
            ProjectDependency.FromPackage("System.Private.ServiceModel", "*"),
            ProjectDependency.FromPackage("System.ServiceModel.Security", "*"),
            ProjectDependency.FromPackage("System.Xml.XmlSerializer", "*"),
            ProjectDependency.FromPackage("System.ServiceModel.Federation", "*"),
            ProjectDependency.FromPackage("System.ServiceModel.NetNamedPipe", "*"),
            ProjectDependency.FromPackage("System.ServiceModel.NetFramingBase", "*")
        };
 
        public static IEnumerable<ProjectDependency> GetWcfProjectReferences(IEnumerable<string> targetFrameworks)
        {
            // If there is no .NET Core or .NET Standard target framework, return FullFrameworkReferences
            if (!targetFrameworks.Any(targetFramework => IsSupportedFramework(targetFramework, out var frameworkInfo) && frameworkInfo.IsDnx))
            {
                return FullFrameworkReferences;
            }
 
            // Determine the lowest .NET (Core) version in the target frameworks (netstandard is excluded by GetLowestNetCoreVersion).
            // Selection uses a >= threshold (not a closed list) so that future .NET versions (net11.0, net12.0, ...) automatically
            // pick the highest currently-supported bucket instead of silently dropping to the Legacy 4.10.* set.
            Version netCoreVersion = GetLowestNetCoreVersion(targetFrameworks);
 
            // Mapping (lowest DNX TFM wins):
            //   >= net10.0  -> Net10  (10.*)
            //   >= net8.0   -> Net8   (8.*)
            //   otherwise   -> Legacy (4.10.*)  -- includes netstandard-only, net6.0/7.0, and mixed older+newer DNX combos.
            if (netCoreVersion != null)
            {
                if (netCoreVersion >= new Version(10, 0))
                {
                    return NetCoreVersionReferenceTable["Net10"];
                }
                if (netCoreVersion >= new Version(8, 0))
                {
                    return NetCoreVersionReferenceTable["Net8"];
                }
            }
 
            return NetCoreVersionReferenceTable["Legacy"];
        }
 
        /// <summary>
        /// From the specified framework collection, find the the framework that would work best for Svcutil (if any).
        /// </summary>
        public static string GetBestFitTargetFramework(IEnumerable<string> targetFrameworks)
        {
            string targetFramework = string.Empty;
            FrameworkInfo fxInfo = null;
 
            if (targetFrameworks != null)
            {
                targetFramework = targetFrameworks.FirstOrDefault((framework) => IsSupportedFramework(framework, out fxInfo) && fxInfo.IsDnx);
                if (targetFramework == null)
                {
                    targetFramework = targetFrameworks.FirstOrDefault((framework) => IsSupportedFramework(framework, out fxInfo));
                    if (targetFramework == null)
                    {
                        targetFramework = targetFrameworks.FirstOrDefault() ?? string.Empty;
                    }
                }
            }
 
            return targetFramework;
        }
 
        public static Version GetLowestNetCoreVersion(IEnumerable<string> targetFrameworks)
        {
            Version targetVersion = null;
 
            foreach (string targetFramework in targetFrameworks)
            {
                if (TargetFrameworkHelper.IsSupportedFramework(targetFramework, out var frameworkInfo) && frameworkInfo.IsDnx && frameworkInfo.Name != FrameworkInfo.Netstandard)
                {
                    if (targetVersion == null || targetVersion > frameworkInfo.Version)
                    {
                        targetVersion = frameworkInfo.Version;
                    }
                }
            }
 
            return targetVersion;
        }
 
        public static FrameworkInfo GetValidFrameworkInfo(string targetFramework)
        {
            if (!IsSupportedFramework(targetFramework, out FrameworkInfo frameworkInfo))
            {
                throw new Exception(string.Format(CultureInfo.CurrentCulture, Shared.Resources.ErrorNotSupportedTargetFrameworkFormat,
                                                  targetFramework, MinSupportedNetCoreAppVersion, MinSupportedNetStandardVersion, MinSupportedNetFxVersionForDotNet));
            }
            return frameworkInfo;
        }
 
        public static bool IsSupportedFramework(string fullFrameworkName, out FrameworkInfo frameworkInfo)
        {
            bool isSupported = false;
 
            if (FrameworkInfo.TryParse(fullFrameworkName, out frameworkInfo))
            {
                isSupported = (frameworkInfo.Name == FrameworkInfo.Netstandard && frameworkInfo.Version >= MinSupportedNetStandardVersion) ||
                              (frameworkInfo.Name == FrameworkInfo.Netcoreapp && frameworkInfo.Version >= MinSupportedNetCoreAppVersion) ||
                              (frameworkInfo.Name == FrameworkInfo.Netfx && frameworkInfo.Version >= MinSupportedNetFxVersionForDotNet);
            }
 
            return isSupported;
        }
 
        public static bool IsEndofLifeFramework(string fullFrameworkName)
        {
            if (FrameworkInfo.TryParse(fullFrameworkName, out FrameworkInfo frameworkInfo))
            {
                // Return true if .NET version is less than the lowest supported Version
                return frameworkInfo.IsDnx
                    && !frameworkInfo.Name.Equals(FrameworkInfo.Netstandard, StringComparison.OrdinalIgnoreCase)
                    && frameworkInfo.Version < new Version(MinSupportedDotNetVersion);
            }
 
            // Return false if parsing fails or no conditions matched
            return false;
        }
 
        public static bool ContainsFullFrameworkTarget(IEnumerable<string> targetFrameworks)
        {
            foreach (string targetFramework in targetFrameworks)
            {
                if (TargetFrameworkHelper.IsSupportedFramework(targetFramework, out var frameworkInfo) && !frameworkInfo.IsDnx)
                {
                    return true;
                }
            }
 
            return false;
        }
    }
}