Support formatter generation for open generic types.
This commit is contained in:
Родитель
7d06c13e72
Коммит
4d19fae633
|
@ -78,7 +78,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Generator", "sr
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.MSBuild.Tasks", "src\MessagePack.MSBuild.Tasks\MessagePack.MSBuild.Tasks.csproj", "{8DB135F5-A6FE-44E4-9853-7B48ED21F21B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePackAnalyzer.Tests", "tests\MessagePackAnalyzer.Tests\MessagePackAnalyzer.Tests.csproj", "{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePackAnalyzer.Tests", "tests\MessagePackAnalyzer.Tests\MessagePackAnalyzer.Tests.csproj", "{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.Generator.Tests", "tests\MessagePack.Generator.Tests\MessagePack.Generator.Tests.csproj", "{6AC51E68-4681-463A-B4B6-BD53517244B2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -270,6 +272,14 @@ Global
|
|||
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Release|NoVSIX.ActiveCfg = Release|Any CPU
|
||||
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A}.Release|NoVSIX.Build.0 = Release|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Debug|NoVSIX.ActiveCfg = Debug|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Debug|NoVSIX.Build.0 = Debug|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|NoVSIX.ActiveCfg = Release|Any CPU
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2}.Release|NoVSIX.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -298,6 +308,7 @@ Global
|
|||
{32C91908-5CAD-4C95-B240-ACBBACAC9476} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
|
||||
{8DB135F5-A6FE-44E4-9853-7B48ED21F21B} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
|
||||
{7E5FB4B9-A0F5-4B10-A1F3-03AC0BC8265A} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
|
||||
{6AC51E68-4681-463A-B4B6-BD53517244B2} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B3911209-2DBF-47F8-98F6-BBC0EDFE63DE}
|
||||
|
|
|
@ -24,10 +24,12 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
|
||||
public string FullName { get; set; }
|
||||
|
||||
public string TemplateParametersString { get; set; }
|
||||
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public string[] GenericTypeParameters { get; set; }
|
||||
|
||||
public bool IsOpenGenericType { get; set; }
|
||||
|
||||
public bool IsIntKey { get; set; }
|
||||
|
||||
public bool IsStringKey
|
||||
|
@ -52,7 +54,7 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
|
||||
public bool NeedsCastOnAfter { get; set; }
|
||||
|
||||
public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter";
|
||||
public string FormatterName => (this.Namespace == null ? this.Name : this.Namespace + "." + this.Name) + "Formatter" + (this.IsOpenGenericType ? $"<{string.Join(",", this.GenericTypeParameters)}>" : string.Empty);
|
||||
|
||||
public int WriteCount
|
||||
{
|
||||
|
|
|
@ -272,6 +272,7 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
private List<GenericSerializationInfo> collectedGenericInfo;
|
||||
private List<UnionSerializationInfo> collectedUnionInfo;
|
||||
private List<ObjectSerializationInfo> collectedClosedTypeGenericInfo;
|
||||
private List<ObjectSerializationInfo> collectedOpenedTypeGenericInfo;
|
||||
|
||||
public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, Action<string> logger)
|
||||
{
|
||||
|
@ -311,10 +312,11 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
this.collectedGenericInfo = new List<GenericSerializationInfo>();
|
||||
this.collectedUnionInfo = new List<UnionSerializationInfo>();
|
||||
this.collectedClosedTypeGenericInfo = new List<ObjectSerializationInfo>();
|
||||
this.collectedOpenedTypeGenericInfo = new List<ObjectSerializationInfo>();
|
||||
}
|
||||
|
||||
// EntryPoint
|
||||
public (ObjectSerializationInfo[] ObjectInfo, EnumSerializationInfo[] EnumInfo, GenericSerializationInfo[] GenericInfo, UnionSerializationInfo[] UnionInfo, ObjectSerializationInfo[] ClosedTypeGenericInfo) Collect()
|
||||
public (ObjectSerializationInfo[] ObjectInfo, EnumSerializationInfo[] EnumInfo, GenericSerializationInfo[] GenericInfo, UnionSerializationInfo[] UnionInfo, ObjectSerializationInfo[] ClosedTypeGenericInfo, ObjectSerializationInfo[] OpenedTypeGenericInfo) Collect()
|
||||
{
|
||||
this.ResetWorkspace();
|
||||
|
||||
|
@ -328,7 +330,8 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
this.collectedEnumInfo.OrderBy(x => x.FullName).ToArray(),
|
||||
this.collectedGenericInfo.Distinct().OrderBy(x => x.FullName).ToArray(),
|
||||
this.collectedUnionInfo.OrderBy(x => x.FullName).ToArray(),
|
||||
this.collectedClosedTypeGenericInfo.OrderBy(x => x.FullName).ToArray());
|
||||
this.collectedClosedTypeGenericInfo.OrderBy(x => x.FullName).ToArray(),
|
||||
this.collectedOpenedTypeGenericInfo.OrderBy(x => x.FullName).ToArray());
|
||||
}
|
||||
|
||||
// Gate of recursive collect
|
||||
|
@ -564,10 +567,11 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
return;
|
||||
}
|
||||
|
||||
// Skip generic symbol declaration itself (open type, e.g. Foo<T>) because we can get nothing useful from it anyways.
|
||||
// Generic types
|
||||
if (type.IsDefinition)
|
||||
{
|
||||
this.CollectGenericUnion(type);
|
||||
this.CollectObject(type);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -608,6 +612,7 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
private ObjectSerializationInfo GetObjectInfo(INamedTypeSymbol type)
|
||||
{
|
||||
var isClass = !type.IsValueType;
|
||||
var isOpenGenericType = type.IsGenericType;
|
||||
|
||||
AttributeData contractAttr = type.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackObjectAttribute));
|
||||
if (contractAttr == null)
|
||||
|
@ -1001,10 +1006,12 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
var info = new ObjectSerializationInfo
|
||||
{
|
||||
IsClass = isClass,
|
||||
IsOpenGenericType = isOpenGenericType,
|
||||
GenericTypeParameters = isOpenGenericType ? type.TypeParameters.Select(x => x.ToDisplayString()).ToArray() : Array.Empty<string>(),
|
||||
ConstructorParameters = constructorParameters.ToArray(),
|
||||
IsIntKey = isIntKey,
|
||||
Members = isIntKey ? intMembers.Values.ToArray() : stringMembers.Values.ToArray(),
|
||||
Name = GetMinimallyQualifiedClassName(type),
|
||||
Name = isOpenGenericType ? GetGenericFormatterClassName(type) : GetMinimallyQualifiedClassName(type),
|
||||
FullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
Namespace = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(),
|
||||
HasIMessagePackSerializationCallbackReceiver = hasSerializationConstructor,
|
||||
|
@ -1015,6 +1022,11 @@ namespace MessagePackCompiler.CodeAnalysis
|
|||
return info;
|
||||
}
|
||||
|
||||
private static string GetGenericFormatterClassName(INamedTypeSymbol type)
|
||||
{
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
private static string GetMinimallyQualifiedClassName(INamedTypeSymbol type)
|
||||
{
|
||||
var name = type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace MessagePackCompiler
|
|||
sw.Restart();
|
||||
logger("Method Collect Start");
|
||||
|
||||
var (objectInfo, enumInfo, genericInfo, unionInfo, closedTypeGenericInfo) = collector.Collect();
|
||||
var (objectInfo, enumInfo, genericInfo, unionInfo, closedTypeGenericInfo, openedTypeGenericInfo) = collector.Collect();
|
||||
|
||||
logger("Method Collect Complete:" + sw.Elapsed.ToString());
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace ");
|
|||
this.Write("\r\n public sealed class ");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.Name));
|
||||
this.Write("Formatter");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.TemplateParametersString != null? objInfo.TemplateParametersString : ""));
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture((objInfo.IsOpenGenericType ? $"<{string.Join(",", objInfo.GenericTypeParameters)}>" : "")));
|
||||
this.Write(" : global::MessagePack.Formatters.IMessagePackFormatter<");
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.FullName));
|
||||
this.Write(">\r\n {\r\n");
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace <#= Namespace #>
|
|||
using MessagePack;
|
||||
<# foreach(var objInfo in ObjectSerializationInfos) { #>
|
||||
|
||||
public sealed class <#= objInfo.Name #>Formatter<#= objInfo.TemplateParametersString != null? objInfo.TemplateParametersString : "" #> : global::MessagePack.Formatters.IMessagePackFormatter<<#= objInfo.FullName #>>
|
||||
public sealed class <#= objInfo.Name #>Formatter<#= (objInfo.IsOpenGenericType ? $"<{string.Join(",", objInfo.GenericTypeParameters)}>" : "") #> : global::MessagePack.Formatters.IMessagePackFormatter<<#= objInfo.FullName #>>
|
||||
{
|
||||
<# foreach(var item in objInfo.Members) { #>
|
||||
<# if(item.CustomFormatterTypeName != null) { #>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MessagePack.Generator.Tests
|
||||
{
|
||||
public class GenerateEnumFormatterTest
|
||||
{
|
||||
private readonly ITestOutputHelper testOutputHelper;
|
||||
|
||||
public GenerateEnumFormatterTest(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
this.testOutputHelper = testOutputHelper;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnumFormatter()
|
||||
{
|
||||
using var tempWorkarea = TemporaryProjectWorkarea.Create();
|
||||
var contents = @"
|
||||
using System;
|
||||
using MessagePack;
|
||||
|
||||
namespace TempProject
|
||||
{
|
||||
[MessagePackObject]
|
||||
public class MyMessagePackObject
|
||||
{
|
||||
[Key(0)]
|
||||
public MyEnum EnumValue { get; set; }
|
||||
}
|
||||
|
||||
public enum MyEnum
|
||||
{
|
||||
A, B, C
|
||||
}
|
||||
}
|
||||
";
|
||||
tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);
|
||||
|
||||
var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
|
||||
await compiler.GenerateFileAsync(
|
||||
tempWorkarea.CsProjectPath,
|
||||
tempWorkarea.OutputDirectory,
|
||||
string.Empty,
|
||||
"TempProjectResolver",
|
||||
"TempProject.Generated",
|
||||
false,
|
||||
string.Empty);
|
||||
|
||||
var compilation = tempWorkarea.GetOutputCompilation();
|
||||
var symbols = compilation.GetNamedTypeSymbolsFromGenerated();
|
||||
symbols.Should().Contain(x => x.Name == "MyEnumFormatter");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MessagePack.Generator.Tests
|
||||
{
|
||||
public class GenerateGenericsFormatterTest
|
||||
{
|
||||
private readonly ITestOutputHelper testOutputHelper;
|
||||
|
||||
public GenerateGenericsFormatterTest(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
this.testOutputHelper = testOutputHelper;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GenericsUnionFormatter()
|
||||
{
|
||||
using var tempWorkarea = TemporaryProjectWorkarea.Create();
|
||||
var contents = @"
|
||||
using System;
|
||||
using MessagePack;
|
||||
|
||||
namespace TempProject
|
||||
{
|
||||
[MessagePackObject]
|
||||
[Union(0, typeof(Wrapper<string>))]
|
||||
[Union(1, typeof(Wrapper<int[]>))]
|
||||
[Union(2, typeof(Wrapper<IEnumerable<Guid>>))]
|
||||
public class Wrapper<T>
|
||||
{
|
||||
[Key(0)]
|
||||
public T Content { get; set; }
|
||||
}
|
||||
}
|
||||
";
|
||||
tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);
|
||||
|
||||
var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
|
||||
await compiler.GenerateFileAsync(
|
||||
tempWorkarea.CsProjectPath,
|
||||
tempWorkarea.OutputDirectory,
|
||||
string.Empty,
|
||||
"TempProjectResolver",
|
||||
"TempProject.Generated",
|
||||
false,
|
||||
string.Empty);
|
||||
|
||||
var compilation = tempWorkarea.GetOutputCompilation();
|
||||
var symbols = compilation.GetNamedTypeSymbolsFromGenerated();
|
||||
var formatters = symbols.SelectMany(x => x.Interfaces).Select(x => x.ToDisplayString()).ToArray();
|
||||
formatters.Should().Contain(x => x == "MessagePack.Formatters.IMessagePackFormatter<TempProject.Wrapper<IEnumerable<System.Guid>>>");
|
||||
formatters.Should().Contain(x => x == "MessagePack.Formatters.IMessagePackFormatter<TempProject.Wrapper<int[]>>");
|
||||
formatters.Should().Contain(x => x == "MessagePack.Formatters.IMessagePackFormatter<TempProject.Wrapper<string>>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GenericsOfTFormatter()
|
||||
{
|
||||
using var tempWorkarea = TemporaryProjectWorkarea.Create(false);
|
||||
var contents = @"
|
||||
using System;
|
||||
using MessagePack;
|
||||
|
||||
namespace TempProject
|
||||
{
|
||||
[MessagePackObject]
|
||||
public class MyGenericObject<T>
|
||||
{
|
||||
[Key(0)]
|
||||
public T Content { get; set; }
|
||||
}
|
||||
}
|
||||
";
|
||||
tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);
|
||||
|
||||
var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
|
||||
await compiler.GenerateFileAsync(
|
||||
tempWorkarea.CsProjectPath,
|
||||
tempWorkarea.OutputDirectory,
|
||||
string.Empty,
|
||||
"TempProjectResolver",
|
||||
"TempProject.Generated",
|
||||
false,
|
||||
string.Empty);
|
||||
|
||||
var compilation = tempWorkarea.GetOutputCompilation();
|
||||
var symbols = compilation.GetNamedTypeSymbolsFromGenerated();
|
||||
|
||||
var types = symbols.Select(x => x.ToDisplayString()).ToArray();
|
||||
types.Should().Contain("TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T>");
|
||||
|
||||
var formatters = symbols.SelectMany(x => x.Interfaces).Select(x => x.ToDisplayString()).ToArray();
|
||||
formatters.Should().Contain(x => x == "MessagePack.Formatters.IMessagePackFormatter<TempProject.MyGenericObject<T>>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GenericsOfT1T2Formatter()
|
||||
{
|
||||
using var tempWorkarea = TemporaryProjectWorkarea.Create();
|
||||
var contents = @"
|
||||
using System;
|
||||
using MessagePack;
|
||||
|
||||
namespace TempProject
|
||||
{
|
||||
[MessagePackObject]
|
||||
public class MyGenericObject<T1, T2>
|
||||
{
|
||||
[Key(0)]
|
||||
public T1 ValueA { get; set; }
|
||||
[Key(1)]
|
||||
public T2 ValueB { get; set; }
|
||||
}
|
||||
}
|
||||
";
|
||||
tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);
|
||||
|
||||
var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
|
||||
await compiler.GenerateFileAsync(
|
||||
tempWorkarea.CsProjectPath,
|
||||
tempWorkarea.OutputDirectory,
|
||||
string.Empty,
|
||||
"TempProjectResolver",
|
||||
"TempProject.Generated",
|
||||
false,
|
||||
string.Empty);
|
||||
|
||||
var compilation = tempWorkarea.GetOutputCompilation();
|
||||
var symbols = compilation.GetNamedTypeSymbolsFromGenerated();
|
||||
|
||||
var types = symbols.Select(x => x.ToDisplayString()).ToArray();
|
||||
types.Should().Contain("TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T1, T2>");
|
||||
|
||||
var formatters = symbols.SelectMany(x => x.Interfaces).Select(x => x.ToDisplayString()).ToArray();
|
||||
formatters.Should().Contain(x => x == "MessagePack.Formatters.IMessagePackFormatter<TempProject.MyGenericObject<T1, T2>>");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>..\MessagePack.Tests\MessagePack.Tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\MessagePack\MessagePack.csproj" />
|
||||
<ProjectReference Include="..\..\src\MessagePack.Annotations\MessagePack.Annotations.csproj" />
|
||||
<ProjectReference Include="..\..\src\MessagePack.GeneratorCore\MessagePack.GeneratorCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright (c) All contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using MessagePack.Formatters;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
|
||||
namespace MessagePack.Generator.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a temporary work area for unit testing.
|
||||
/// </summary>
|
||||
public class TemporaryProjectWorkarea : IDisposable
|
||||
{
|
||||
private readonly string tempDirPath;
|
||||
private readonly string csprojFileName = "TempProject.csproj";
|
||||
private readonly bool cleanOnDisposing;
|
||||
|
||||
public string CsProjectPath { get; }
|
||||
|
||||
public string ProjectDirectory { get; }
|
||||
|
||||
public string OutputDirectory { get; }
|
||||
|
||||
public static TemporaryProjectWorkarea Create(bool cleanOnDisposing = true)
|
||||
{
|
||||
return new TemporaryProjectWorkarea(cleanOnDisposing);
|
||||
}
|
||||
|
||||
private TemporaryProjectWorkarea(bool cleanOnDisposing)
|
||||
{
|
||||
this.cleanOnDisposing = cleanOnDisposing;
|
||||
this.tempDirPath = Path.Combine(Path.GetTempPath(), $"MessagePack.Generator.Tests-{Guid.NewGuid()}");
|
||||
|
||||
ProjectDirectory = Path.Combine(tempDirPath, "Project");
|
||||
OutputDirectory = Path.Combine(tempDirPath, "Output");
|
||||
|
||||
Directory.CreateDirectory(ProjectDirectory);
|
||||
Directory.CreateDirectory(OutputDirectory);
|
||||
|
||||
var solutionRootDir = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../../../.."));
|
||||
var messagePackProjectDir = Path.Combine(solutionRootDir, "src/MessagePack/MessagePack.csproj");
|
||||
var annotationsProjectDir = Path.Combine(solutionRootDir, "src/MessagePack.Annotations/MessagePack.Annotations.csproj");
|
||||
|
||||
CsProjectPath = Path.Combine(ProjectDirectory, csprojFileName);
|
||||
var csprojContents = @"
|
||||
<Project Sdk=""Microsoft.NET.Sdk"">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include=""" + messagePackProjectDir + @""" />
|
||||
<ProjectReference Include=""" + annotationsProjectDir + @""" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
";
|
||||
AddFileToProject(csprojFileName, csprojContents);
|
||||
}
|
||||
|
||||
public void AddFileToProject(string fileName, string contents)
|
||||
{
|
||||
File.WriteAllText(Path.Combine(ProjectDirectory, fileName), contents.Trim());
|
||||
}
|
||||
|
||||
public CompilationContainer GetOutputCompilation()
|
||||
{
|
||||
var compilation = CSharpCompilation.Create(Guid.NewGuid().ToString())
|
||||
.AddSyntaxTrees(
|
||||
Directory.EnumerateFiles(ProjectDirectory, "*.cs", SearchOption.AllDirectories)
|
||||
.Concat(Directory.EnumerateFiles(OutputDirectory, "*.cs", SearchOption.AllDirectories))
|
||||
.Select(x => CSharpSyntaxTree.ParseText(File.ReadAllText(x), CSharpParseOptions.Default, x)))
|
||||
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
|
||||
.AddReferences(MetadataReference.CreateFromFile(typeof(MessagePack.MessagePackObjectAttribute).Assembly.Location))
|
||||
.AddReferences(MetadataReference.CreateFromFile(typeof(IMessagePackFormatter<>).Assembly.Location));
|
||||
|
||||
return new CompilationContainer(compilation);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (cleanOnDisposing)
|
||||
{
|
||||
Directory.Delete(tempDirPath, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CompilationContainer
|
||||
{
|
||||
public Compilation Compilation { get; }
|
||||
|
||||
public CompilationContainer(Compilation compilation)
|
||||
{
|
||||
this.Compilation = compilation ?? throw new ArgumentNullException(nameof(compilation));
|
||||
}
|
||||
|
||||
public INamedTypeSymbol[] GetNamedTypeSymbolsFromGenerated()
|
||||
{
|
||||
return Compilation.SyntaxTrees
|
||||
.Select(x => Compilation.GetSemanticModel(x))
|
||||
.SelectMany(semanticModel =>
|
||||
{
|
||||
return semanticModel.SyntaxTree.GetRoot()
|
||||
.DescendantNodes()
|
||||
.Select(x => semanticModel.GetDeclaredSymbol(x))
|
||||
.OfType<INamedTypeSymbol>();
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче