|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Extensions.DependencyModel;
using Xunit;
namespace Microsoft.CodeAnalysis;
public static class TestCompilation
{
private static readonly ConcurrentDictionary<Assembly, IEnumerable<MetadataReference>> _referenceCache =
new ConcurrentDictionary<Assembly, IEnumerable<MetadataReference>>();
public static IEnumerable<MetadataReference> GetMetadataReferences(Assembly assembly)
{
var dependencyContext = DependencyContext.Load(assembly);
var metadataReferences = dependencyContext.CompileLibraries
.SelectMany(l => ResolvePaths(l))
.Select(assemblyPath => MetadataReference.CreateFromFile(assemblyPath))
.ToArray();
return metadataReferences;
}
private static IEnumerable<string> ResolvePaths(CompilationLibrary library)
{
#if NETFRAMEWORK
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (var i = 0; i < assemblies.Length; i++)
{
if (assemblies[i].GetName().Name == library.Name)
{
return new[] { assemblies[i].Location };
}
}
#endif
try
{
return library.ResolveReferencePaths();
}
catch (InvalidOperationException)
{
}
return Array.Empty<string>();
}
public static string AssemblyName => "TestAssembly";
// When we use Basic.Reference.Assemblies everywhere, this overload should be removed.
// See also https://github.com/dotnet/razor/issues/10343.
public static CSharpCompilation Create(Assembly assembly, SyntaxTree syntaxTree = null)
{
IEnumerable<SyntaxTree> syntaxTrees = null;
if (syntaxTree != null)
{
syntaxTrees = new[] { syntaxTree };
}
if (!_referenceCache.TryGetValue(assembly, out var metadataReferences))
{
metadataReferences = GetMetadataReferences(assembly);
_referenceCache.TryAdd(assembly, metadataReferences);
}
var compilation = CSharpCompilation.Create(AssemblyName, syntaxTrees, metadataReferences, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
EnsureValidCompilation(compilation);
return compilation;
}
public static CSharpCompilation Create() => Create(syntaxTrees: [], references: []);
public static CSharpCompilation Create(IEnumerable<SyntaxTree> syntaxTrees, IEnumerable<MetadataReference> references)
{
var compilation = CSharpCompilation.Create(
AssemblyName,
syntaxTrees,
[..references, ..ReferenceUtil.AspNetLatestAll],
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
EnsureValidCompilation(compilation);
return compilation;
}
private static void EnsureValidCompilation(CSharpCompilation compilation)
{
using (var stream = new MemoryStream())
{
var emitResult = compilation
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.Emit(stream);
var diagnostics = string.Join(
Environment.NewLine,
emitResult.Diagnostics.Select(d => CSharpDiagnosticFormatter.Instance.Format(d)));
Assert.True(emitResult.Success, $"Compilation is invalid : {Environment.NewLine}{diagnostics}");
}
}
}
|