|
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Basic.Reference.Assemblies;
using static TestReferences;
using static Roslyn.Test.Utilities.TestMetadata;
using Microsoft.CodeAnalysis.CodeGen;
using System.Reflection;
using System.Collections.Concurrent;
namespace Roslyn.Test.Utilities
{
public enum TargetFramework
{
/// <summary>
/// Explicit pick a target framework that has no references
/// </summary>
Empty,
NetStandard20,
/// <summary>
/// The latest .NET Core target framework
/// </summary>
NetCoreApp,
/// <summary>
/// The latest .NET Framework
/// </summary>
NetFramework,
/// <summary>
/// This will be <see cref="NetCoreApp" /> when running on .NET Core and <see cref="NetFramework"/>
/// when running on .NET Framework.
/// </summary>
NetLatest,
// Eventually these will be deleted and replaced with NetStandard20. Short term this creates the "standard"
// API set across desktop and coreclr. It's also helpful because there are no null annotations hence error
// messages have consistent signatures across .NET Core / Framework tests.
Standard,
StandardAndCSharp,
StandardAndVBRuntime,
/// <summary>
/// Compat framework for the default set of references many vb compilations get.
/// </summary>
DefaultVb,
/// <summary>
/// Used for building tests against WinRT scenarios
/// </summary>
WinRT,
// The flavors of mscorlib we support + extending them with LINQ and dynamic.
Mscorlib40,
Mscorlib40Extended,
Mscorlib40AndSystemCore,
Mscorlib40AndVBRuntime,
Mscorlib45,
Mscorlib45Extended,
Mscorlib45AndCSharp,
Mscorlib45AndVBRuntime,
Mscorlib46,
Mscorlib46Extended,
Mscorlib461,
Mscorlib461Extended,
DesktopLatestExtended = Mscorlib461Extended,
/// <summary>
/// Minimal set of required types (<see cref="NetFx.Minimal.mincorlib"/>).
/// </summary>
Minimal,
/// <summary>
/// Minimal set of required types and Task implementation (<see cref="NetFx.Minimal.minasync"/>).
/// </summary>
MinimalAsync,
Net50,
Net60,
Net70
}
/// <summary>
/// This type holds the reference information for the latest .NET Core platform. Tests
/// targeting .NET core specifically should use the references here. As the platform moves
/// forward these will be moved to target the latest .NET Core supported by the compiler
/// </summary>
public static class NetCoreApp
{
public static ImmutableArray<Net70.ReferenceInfo> AllReferenceInfos { get; } = ImmutableArray.CreateRange(Net70.References.All);
public static ImmutableArray<MetadataReference> References { get; } = ImmutableArray.CreateRange<MetadataReference>(Net70.All);
public static PortableExecutableReference netstandard { get; } = Net70.netstandard;
public static PortableExecutableReference mscorlib { get; } = Net70.mscorlib;
public static PortableExecutableReference SystemRuntime { get; } = Net70.SystemRuntime;
}
/// <summary>
/// This type holds the reference information for the latest .NET Framework. These should be
/// used by tests that are specific to .NET Framework. This moves forward much more rarely but
/// when it does move forward these will change
/// </summary>
public static class NetFramework
{
/// <summary>
/// This is the full set of references provided by default on the .NET Framework TFM
/// </summary>
/// <remarks>
/// Need to special case tuples until we move to net472
/// </remarks>
public static ImmutableArray<MetadataReference> References { get; } =
ImmutableArray
.CreateRange<MetadataReference>(Net461.All)
.Add(NetFx.ValueTuple.tuplelib);
/// <summary>
/// This is a limited set of references on this .NET Framework TFM. This should be avoided in new code
/// as it represents the way reference hookup used to work.
/// </summary>
/// <remarks>
/// Need to special case tuples until we move to net472
/// </remarks>
public static ImmutableArray<MetadataReference> Standard { get; } =
ImmutableArray.Create<MetadataReference>(
Net461.mscorlib,
Net461.System,
Net461.SystemCore,
NetFx.ValueTuple.tuplelib,
Net461.SystemRuntime);
public static PortableExecutableReference mscorlib { get; } = Net461.mscorlib;
public static PortableExecutableReference System { get; } = Net461.System;
public static PortableExecutableReference SystemRuntime { get; } = Net461.SystemRuntime;
public static PortableExecutableReference SystemCore { get; } = Net461.SystemCore;
public static PortableExecutableReference SystemThreadingTasks { get; } = Net461.SystemThreadingTasks;
public static PortableExecutableReference MicrosoftCSharp { get; } = Net461.MicrosoftCSharp;
public static PortableExecutableReference MicrosoftVisualBasic { get; } = Net461.MicrosoftVisualBasic;
}
public static class TargetFrameworkUtil
{
private static readonly ConcurrentDictionary<string, ImmutableArray<PortableExecutableReference>> s_dynamicReferenceMap = new ConcurrentDictionary<string, ImmutableArray<PortableExecutableReference>>(StringComparer.Ordinal);
public static ImmutableArray<MetadataReference> NetLatest => RuntimeUtilities.IsCoreClrRuntime ? NetCoreApp.References : NetFramework.References;
public static ImmutableArray<MetadataReference> StandardReferences => RuntimeUtilities.IsCoreClrRuntime ? NetStandard20References : NetFramework.Standard;
public static MetadataReference StandardCSharpReference => RuntimeUtilities.IsCoreClrRuntime ? MicrosoftCSharp.Netstandard13Lib : NetFramework.MicrosoftCSharp;
public static MetadataReference StandardVisualBasicReference => RuntimeUtilities.IsCoreClrRuntime ? MicrosoftVisualBasic.Netstandard11 : NetFramework.MicrosoftVisualBasic;
public static ImmutableArray<MetadataReference> StandardAndCSharpReferences => StandardReferences.Add(StandardCSharpReference);
public static ImmutableArray<MetadataReference> StandardAndVBRuntimeReferences => StandardReferences.Add(StandardVisualBasicReference);
/*
* ⚠ Dev note ⚠: properties in TestBase are backed by Lazy<T>. Avoid changes to the following properties
* which would force the initialization of these properties in the static constructor, since the stack traces
* for a TypeLoadException are missing important information for resolving problems if/when they occur.
* https://github.com/dotnet/roslyn/issues/25961
*/
public static ImmutableArray<MetadataReference> Mscorlib40References => ImmutableArray.Create<MetadataReference>(Net40.mscorlib);
public static ImmutableArray<MetadataReference> Mscorlib40ExtendedReferences => ImmutableArray.Create<MetadataReference>(Net40.mscorlib, Net40.System, Net40.SystemCore);
public static ImmutableArray<MetadataReference> Mscorlib40andSystemCoreReferences => ImmutableArray.Create<MetadataReference>(Net40.mscorlib, Net40.SystemCore);
public static ImmutableArray<MetadataReference> Mscorlib40andVBRuntimeReferences => ImmutableArray.Create<MetadataReference>(Net40.mscorlib, Net40.System, Net40.MicrosoftVisualBasic);
public static ImmutableArray<MetadataReference> Mscorlib45References => ImmutableArray.Create<MetadataReference>(Net451.mscorlib);
public static ImmutableArray<MetadataReference> Mscorlib45ExtendedReferences => ImmutableArray.Create<MetadataReference>(Net451.mscorlib, Net451.System, Net451.SystemCore, TestBase.ValueTupleRef, Net451.SystemRuntime);
public static ImmutableArray<MetadataReference> Mscorlib45AndCSharpReferences => ImmutableArray.Create<MetadataReference>(Net451.mscorlib, Net451.SystemCore, Net451.MicrosoftCSharp);
public static ImmutableArray<MetadataReference> Mscorlib45AndVBRuntimeReferences => ImmutableArray.Create<MetadataReference>(Net451.mscorlib, Net451.System, Net451.MicrosoftVisualBasic);
public static ImmutableArray<MetadataReference> Mscorlib46References => ImmutableArray.Create<MetadataReference>(Net461.mscorlib);
public static ImmutableArray<MetadataReference> Mscorlib46ExtendedReferences => ImmutableArray.Create<MetadataReference>(Net461.mscorlib, Net461.System, Net461.SystemCore, TestBase.ValueTupleRef, Net461.SystemRuntime);
public static ImmutableArray<MetadataReference> Mscorlib461References => ImmutableArray.Create<MetadataReference>(Net461.mscorlib);
public static ImmutableArray<MetadataReference> Mscorlib461ExtendedReferences => ImmutableArray.Create<MetadataReference>(Net461.mscorlib, Net461.System, Net461.SystemCore, NetFx.ValueTuple.tuplelib, Net461.SystemRuntime);
public static ImmutableArray<MetadataReference> NetStandard20References => ImmutableArray.Create<MetadataReference>(NetStandard20.netstandard, NetStandard20.mscorlib, NetStandard20.SystemRuntime, NetStandard20.SystemCore, NetStandard20.SystemDynamicRuntime, NetStandard20.SystemLinq, NetStandard20.SystemLinqExpressions);
public static ImmutableArray<MetadataReference> WinRTReferences => ImmutableArray.Create(TestBase.WinRtRefs);
public static ImmutableArray<MetadataReference> DefaultVbReferences => ImmutableArray.Create<MetadataReference>(Net451.mscorlib, Net451.System, Net451.SystemCore, Net451.MicrosoftVisualBasic);
public static ImmutableArray<MetadataReference> MinimalReferences => ImmutableArray.Create(TestBase.MinCorlibRef);
public static ImmutableArray<MetadataReference> MinimalAsyncReferences => ImmutableArray.Create(TestBase.MinAsyncCorlibRef);
#if DEBUG
static TargetFrameworkUtil()
{
// Asserts to ensure these two values keep in sync
Debug.Assert(GetReferences(TargetFramework.NetCoreApp).SequenceEqual(NetCoreApp.References));
Debug.Assert(GetReferences(TargetFramework.NetFramework).SequenceEqual(NetFramework.References));
}
#endif
public static ImmutableArray<MetadataReference> GetReferences(TargetFramework targetFramework) => targetFramework switch
{
// Primary
// Note: NetCoreApp should behave like latest Core TFM
TargetFramework.Empty => ImmutableArray<MetadataReference>.Empty,
TargetFramework.NetStandard20 => NetStandard20References,
TargetFramework.Net50 => ImmutableArray.CreateRange<MetadataReference>(LoadDynamicReferences("Net50")),
TargetFramework.Net60 => ImmutableArray.CreateRange<MetadataReference>(LoadDynamicReferences("Net60")),
TargetFramework.NetCoreApp or TargetFramework.Net70 => ImmutableArray.CreateRange<MetadataReference>(Net70.All),
TargetFramework.NetFramework => NetFramework.References,
TargetFramework.NetLatest => NetLatest,
TargetFramework.Standard => StandardReferences,
// Legacy we should be phasing out
TargetFramework.Mscorlib40 => Mscorlib40References,
TargetFramework.Mscorlib40Extended => Mscorlib40ExtendedReferences,
TargetFramework.Mscorlib40AndSystemCore => Mscorlib40andSystemCoreReferences,
TargetFramework.Mscorlib40AndVBRuntime => Mscorlib40andVBRuntimeReferences,
TargetFramework.Mscorlib45 => Mscorlib45References,
TargetFramework.Mscorlib45Extended => Mscorlib45ExtendedReferences,
TargetFramework.Mscorlib45AndCSharp => Mscorlib45AndCSharpReferences,
TargetFramework.Mscorlib45AndVBRuntime => Mscorlib45AndVBRuntimeReferences,
TargetFramework.Mscorlib46 => Mscorlib46References,
TargetFramework.Mscorlib46Extended => Mscorlib46ExtendedReferences,
TargetFramework.Mscorlib461 => Mscorlib46References,
TargetFramework.Mscorlib461Extended => Mscorlib461ExtendedReferences,
TargetFramework.WinRT => WinRTReferences,
TargetFramework.StandardAndCSharp => StandardAndCSharpReferences,
TargetFramework.StandardAndVBRuntime => StandardAndVBRuntimeReferences,
TargetFramework.DefaultVb => DefaultVbReferences,
TargetFramework.Minimal => MinimalReferences,
TargetFramework.MinimalAsync => MinimalAsyncReferences,
_ => throw new InvalidOperationException($"Unexpected target framework {targetFramework}"),
};
public static ImmutableArray<MetadataReference> GetReferences(TargetFramework tf, IEnumerable<MetadataReference> additionalReferences)
{
var references = GetReferences(tf);
if (additionalReferences == null)
{
return references;
}
checkForDuplicateReferences();
return references.AddRange(additionalReferences);
// Check to see if there are any duplicate references. This guards against tests inadvertently passing multiple copies of
// say System.Core to the tests and implicitly depending on the higher one to win. The few tests which actually mean to
// pass multiple versions of a DLL should manually construct the reference list and not use this helper.
void checkForDuplicateReferences()
{
var nameSet = new HashSet<string>(getNames(references), StringComparer.OrdinalIgnoreCase);
foreach (var r in additionalReferences)
{
if (references.Contains(r))
{
throw new Exception($"Duplicate reference detected {r.Display}");
}
var name = getName(r);
if (name != null && !nameSet.Add(name))
{
throw new Exception($"Duplicate reference detected {r.Display} - {name}");
}
}
}
IEnumerable<string> getNames(IEnumerable<MetadataReference> e)
{
foreach (var r in e)
{
var name = getName(r);
if (name != null)
{
yield return name;
}
}
}
string getName(MetadataReference m)
{
if (m is PortableExecutableReference p &&
p.GetMetadata() is AssemblyMetadata assemblyMetadata)
{
try
{
var identity = assemblyMetadata.GetAssembly().Identity;
return identity?.Name;
}
catch (BadImageFormatException)
{
// Happens when a native image is incorrectly passed as a PE.
return null;
}
}
return null;
}
}
public static IEnumerable<MetadataReference> GetReferencesWithout(TargetFramework targetFramework, params string[] excludeReferenceNames) =>
GetReferences(targetFramework)
.Where(x => !(x is PortableExecutableReference pe && excludeReferenceNames.Contains(pe.FilePath)));
/// <summary>
/// Many of our reference assemblies are only used by a subset of compiler unit tests. Having a PackageReference
/// to the assemblies here would cause them to be deployed to every unit test we write though. These are non-trivial
/// in size (4+ MB) and we have ~50 test projects so this adds up fast. To keep size down we just add
/// PackageReference on the few projects that and dynamically load here.
/// </summary>
private static ImmutableArray<PortableExecutableReference> LoadDynamicReferences(string targetFrameworkName)
{
var assemblyName = $"Basic.Reference.Assemblies.{targetFrameworkName}";
if (s_dynamicReferenceMap.TryGetValue(assemblyName, out var references))
{
return references;
}
try
{
var name = new AssemblyName(assemblyName);
var assembly = Assembly.Load(name);
var type = assembly.GetType(assemblyName, throwOnError: true);
var prop = type.GetProperty("All", BindingFlags.Public | BindingFlags.Static);
var obj = prop.GetGetMethod()!.Invoke(obj: null, parameters: null);
references = ((IEnumerable<PortableExecutableReference>)obj).ToImmutableArray();
// This method can de called in parallel. Who wins this TryAdd isn't important, it's the same
// values.
_ = s_dynamicReferenceMap.TryAdd(assemblyName, references);
return references;
}
catch (Exception ex)
{
var message = $"Error loading {assemblyName}. Make sure the test project has a <PackageReference> for this assembly";
throw new Exception(message, ex);
}
}
}
}
|