#pragma warning disable SA1512,SA1124 // Single-line comments should not be followed by blank line #if UNITY_2021_2_OR_NEWER using UnityEditor.Build; #endif using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; using NugetForUnity.Models; using UnityEditor; #region No ReShaper // ReSharper disable All // needed because 'JetBrains.Annotations.NotNull' and 'System.Diagnostics.CodeAnalysis.NotNull' collide if this file is compiled with a never version of Unity / C# using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; // ReSharper restore All #endregion #pragma warning restore SA1512,SA1124 // Single-line comments should not be followed by blank line namespace NugetForUnity { /// /// Helper to select the best target-framework. /// internal static class TargetFrameworkResolver { // highest priority first. We use values without '.' for easier comparison. [NotNull] private static readonly TargetFrameworkSupport[] PrioritizedTargetFrameworks = { new TargetFrameworkSupport("unity"), // .net framework (as we don't support unity < 2018 we can expect that at least .net framework 4.4 is supported) new TargetFrameworkSupport("net48", new UnityVersion(2021, 2, 0, 'f', 0), DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net472", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net471", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net47", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net462", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net461", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net46", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net452", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net451", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net45", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net403", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net40", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net4", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net35-unity full v35", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net35-unity subset v35", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net35", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net20", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport("net11", null, DotnetVersionCompatibilityLevel.NetFramework46Or48), // .net standard new TargetFrameworkSupport( "netstandard21", new UnityVersion(2021, 2, 0, 'f', 0), DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard20", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard16", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard15", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard14", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard13", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard12", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard11", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), new TargetFrameworkSupport( "netstandard10", null, DotnetVersionCompatibilityLevel.NetStandard20Or21, DotnetVersionCompatibilityLevel.NetFramework46Or48), // fallback if there is one with empty string new TargetFrameworkSupport(string.Empty), }; /// /// Gets the of the current selected build target. /// [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local", Justification = "Property setter needed for unit test")] internal static Lazy CurrentBuildTargetApiCompatibilityLevel { get; private set; } = new Lazy( () => { #if UNITY_2021_2_OR_NEWER return PlayerSettings.GetApiCompatibilityLevel( NamedBuildTarget.FromBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup)); #else return PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup); #endif }); private static DotnetVersionCompatibilityLevel CurrentBuildTargetDotnetVersionCompatibilityLevel { get { switch (CurrentBuildTargetApiCompatibilityLevel.Value) { case ApiCompatibilityLevel.NET_4_6: return DotnetVersionCompatibilityLevel.NetFramework46Or48; case ApiCompatibilityLevel.NET_Standard_2_0: return DotnetVersionCompatibilityLevel.NetStandard20Or21; default: return DotnetVersionCompatibilityLevel.None; } } } /// /// Select the highest .NET library available that is supported by Unity. /// See here: https://docs.nuget.org/ndocs/schema/target-frameworks. /// /// The list of available target-frameworks. /// /// The overwritten / preferred target-framework to use instead of determining the best matching target framework /// from the Unity settings ('Api Compatibility Level'). /// /// The best matching target-framework. [CanBeNull] public static string TryGetBestTargetFramework( [NotNull] [ItemNotNull] IReadOnlyCollection availableTargetFrameworks, [CanBeNull] string preferredTargetFramework) { return TryGetBestTargetFramework(availableTargetFrameworks, preferredTargetFramework, targetFramework => targetFramework); } /// /// Select the highest .NET library available that is supported by Unity. /// See here: https://docs.nuget.org/ndocs/schema/target-frameworks. /// /// The type of the target-framework. /// The list of available target-frameworks. /// /// The overwritten / preferred target-framework to use instead of determining the best matching target framework /// from the Unity settings ('Api Compatibility Level'). /// /// A function to get the target-framework string. /// The best matching target-framework. [CanBeNull] public static T TryGetBestTargetFramework( [NotNull] [ItemNotNull] IReadOnlyCollection availableTargetFrameworks, [CanBeNull] string preferredTargetFramework, [NotNull] Func getTargetFrameworkString) { if (!string.IsNullOrEmpty(preferredTargetFramework)) { preferredTargetFramework = preferredTargetFramework.Replace(".", string.Empty); var bestMatch = availableTargetFrameworks.FirstOrDefault( availableTargetFramework => { var availableString = getTargetFrameworkString(availableTargetFramework).Replace(".", string.Empty); return availableString.Equals(preferredTargetFramework, StringComparison.OrdinalIgnoreCase); }); if (!Equals(bestMatch, default(T))) { return bestMatch; } } var currentDotnetVersion = CurrentBuildTargetDotnetVersionCompatibilityLevel; var currentUnityVersion = UnityVersion.Current; foreach (var targetFrameworkSupport in PrioritizedTargetFrameworks) { if (targetFrameworkSupport.SupportedDotnetVersions.Length != 0 && !targetFrameworkSupport.SupportedDotnetVersions.Contains(currentDotnetVersion)) { continue; } if (targetFrameworkSupport.MinimumUnityVersion != null && currentUnityVersion < targetFrameworkSupport.MinimumUnityVersion.Value) { continue; } var bestMatch = availableTargetFrameworks.FirstOrDefault( availableTargetFramework => { var availableString = getTargetFrameworkString(availableTargetFramework).Replace(".", string.Empty); return availableString.Equals(targetFrameworkSupport.Name, StringComparison.OrdinalIgnoreCase); }); if (!Equals(bestMatch, default(T))) { return bestMatch; } } return default; } /// /// Select the best target-framework group of a NuGet package. /// /// The available frameworks. /// /// The overwritten / preferred target-framework to use instead of determining the best matching target framework /// from the Unity settings ('Api Compatibility Level'). /// /// The selected target framework group or null if non is matching. [CanBeNull] internal static NugetFrameworkGroup GetNullableBestDependencyFrameworkGroupForCurrentSettings( [NotNull] [ItemNotNull] List packageDependencies, [CanBeNull] string preferredTargetFramework) { var bestTargetFramework = TryGetBestTargetFramework( packageDependencies, preferredTargetFramework, frameworkGroup => frameworkGroup.TargetFramework); NugetLogger.LogVerbose( "Selecting {0} as the best target framework for current settings", bestTargetFramework?.TargetFramework ?? "(null)"); return bestTargetFramework; } /// /// Select the best target-framework group of a NuGet package. /// /// The available frameworks. /// /// The overwritten / preferred target-framework to use instead of determining the best matching target framework /// from the Unity settings ('Api Compatibility Level'). /// /// The selected target framework group or a empty group if non is matching. [NotNull] internal static NugetFrameworkGroup GetBestDependencyFrameworkGroupForCurrentSettings( [NotNull] [ItemNotNull] List packageDependencies, [CanBeNull] string preferredTargetFramework) { return GetNullableBestDependencyFrameworkGroupForCurrentSettings(packageDependencies, preferredTargetFramework) ?? new NugetFrameworkGroup(); } /// /// Select the best target-framework group of a NuGet package. /// /// The package of witch the dependencies are selected. /// /// The overwritten / preferred target-framework to use instead of determining the best matching target framework /// from the Unity settings ('Api Compatibility Level'). /// /// The selected target framework group or a empty group if non is matching. [NotNull] internal static NugetFrameworkGroup GetBestDependencyFrameworkGroupForCurrentSettings( [NotNull] NuspecFile nuspec, [CanBeNull] string preferredTargetFramework) { return GetBestDependencyFrameworkGroupForCurrentSettings(nuspec.Dependencies, preferredTargetFramework); } /// /// Select the best target-framework name. /// /// The available frameworks. /// /// The overwritten / preferred target-framework to use instead of determining the best matching target framework /// from the Unity settings ('Api Compatibility Level'). /// /// The selected target framework or null if non is matching. [CanBeNull] internal static string TryGetBestTargetFrameworkForCurrentSettings( [NotNull] [ItemNotNull] IReadOnlyCollection targetFrameworks, [CanBeNull] string preferredTargetFramework) { var result = TryGetBestTargetFramework(targetFrameworks, preferredTargetFramework); NugetLogger.LogVerbose("Selecting {0} as the best target framework for current settings", result ?? "(null)"); return result; } [SuppressMessage( "StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "We like private enums at the botom of the file.")] private enum DotnetVersionCompatibilityLevel { None = 0, /// /// The full .net framework (not .net core or normal .net since (.net 5.0)). /// .net framework 4.6 available since Unity 5.6 and Unity >= 2017.1. /// .net framework 4.8 since Unity 2021.2. /// NetFramework46Or48, /// /// .NET Standard is a formal specification of .NET APIs that are available on multiple .NET implementations. /// Unity supports .net standard 2.0 and since Unity 2021.2 .net standard 2.1. /// NetStandard20Or21, } // Ignore Spelling: dotnet private readonly struct TargetFrameworkSupport { public TargetFrameworkSupport( string name, UnityVersion? minimumUnityVersion = null, params DotnetVersionCompatibilityLevel[] supportedDotnetVersions) { Name = name; MinimumUnityVersion = minimumUnityVersion; SupportedDotnetVersions = supportedDotnetVersions; } public UnityVersion? MinimumUnityVersion { get; } public string Name { get; } public DotnetVersionCompatibilityLevel[] SupportedDotnetVersions { get; } } } }