Skip to content

Commit

Permalink
Merge pull request #15 from dotnet-campus/t/lindexi/Compile
Browse files Browse the repository at this point in the history
尝试对接源代码生成
  • Loading branch information
lindexi authored Nov 19, 2022
2 parents fc2c33d + ac7ae80 commit a883b5e
Show file tree
Hide file tree
Showing 15 changed files with 361 additions and 18 deletions.
18 changes: 15 additions & 3 deletions YamlToCSharp.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
# Visual Studio Version 17
VisualStudioVersion = 17.2.32630.192
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.YamlToCSharp.Build", "src\dotnetCampus.YamlToCSharp.Build\dotnetCampus.YamlToCSharp.Build.csproj", "{D1F53894-4EF0-4BC5-A4DF-64006D6E8E71}"
EndProject
Expand All @@ -16,7 +16,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
build\Version.props = build\Version.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.YamlToCSharp.Sample.Wpf", "samples\dotnetCampus.YamlToCSharp.Sample.Wpf\dotnetCampus.YamlToCSharp.Sample.Wpf.csproj", "{9B5465E9-F68D-4287-AC02-8C848B69DA79}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.YamlToCSharp.Sample.Wpf", "samples\dotnetCampus.YamlToCSharp.Sample.Wpf\dotnetCampus.YamlToCSharp.Sample.Wpf.csproj", "{9B5465E9-F68D-4287-AC02-8C848B69DA79}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.YamlToCSharp.SourceGeneration.Sample", "samples\dotnetCampus.YamlToCSharp.SourceGeneration.Sample\dotnetCampus.YamlToCSharp.SourceGeneration.Sample.csproj", "{E3BDEF1C-F36D-4E4E-8CD3-179CACC8D9A3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnetCampus.YamlToCSharp.Analyzers", "src\dotnetCampus.YamlToCSharp.Analyzers\dotnetCampus.YamlToCSharp.Analyzers.csproj", "{C7256C2F-BA20-474C-8FD3-99C0556CFBE2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -36,6 +40,14 @@ Global
{9B5465E9-F68D-4287-AC02-8C848B69DA79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B5465E9-F68D-4287-AC02-8C848B69DA79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B5465E9-F68D-4287-AC02-8C848B69DA79}.Release|Any CPU.Build.0 = Release|Any CPU
{E3BDEF1C-F36D-4E4E-8CD3-179CACC8D9A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3BDEF1C-F36D-4E4E-8CD3-179CACC8D9A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3BDEF1C-F36D-4E4E-8CD3-179CACC8D9A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3BDEF1C-F36D-4E4E-8CD3-179CACC8D9A3}.Release|Any CPU.Build.0 = Release|Any CPU
{C7256C2F-BA20-474C-8FD3-99C0556CFBE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7256C2F-BA20-474C-8FD3-99C0556CFBE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7256C2F-BA20-474C-8FD3-99C0556CFBE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7256C2F-BA20-474C-8FD3-99C0556CFBE2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Extension:
Name: Extension
Extra:
A: "Option A"
B: "Option B"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
App:
Name: dotnetCampus.YamlToCSharp.Sample.Wpf
AboutUs: "About Us"
Common:
Yes: "Yes"
No: "No"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Extension:
Name: 扩展程序
Extra:
A: "选项 A"
B: "选项 B"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
App:
Name: "WPF 版 YAML 转 C# 程序"
AboutUs: "关于我们"
Common:
Yes: ""
No: ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Extension:
Name: 擴展程序
Extra:
A: "選項 A"
B: "選項 B"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
App:
Name: "WPF 版 YAML 轉 C# 程序"
AboutUs: "關於我們"
Common:
Yes: ""
No: ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;

namespace dotnetCampus.YamlToCSharp.SourceGeneration.Sample
{
class Program
{
static void Main(string[] args)
{
var languages = new Dictionary<string, Dictionary<string, string>[]>()
{
{
"zh-CN", new[]
{
new Localizations.zh_CN.Main().GetLang(),
new Localizations.zh_CN.Extension().GetLang(),
}
},
{
"zh-TW", new[]
{
new Localizations.zh_TW.Main().GetLang(),
new Localizations.zh_TW.Extension().GetLang(),
}
},
{
"en-US", new[]
{
new Localizations.en_US.Main().GetLang(),
new Localizations.en_US.Extension().GetLang(),
}
},
};

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<Import Project="..\..\src\dotnetCampus.YamlToCSharp.Analyzers\Assets\build\PackageId.props" />

<ItemGroup>
<ProjectReference Include="..\..\src\dotnetCampus.YamlToCSharp.Analyzers\dotnetCampus.YamlToCSharp.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project>

<!--<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>-->

<ItemGroup>
<YamlToCSharpCompile Include="**\*.yml;**\*.yaml" Exclude="obj\**\*.yml;obj\**\*.yaml;bin\**\*.yml;bin\**\*.yaml" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="@(YamlToCSharpCompile)" />
<CompilerVisibleProperty Include="MSBuildProjectDirectory" />
<CompilerVisibleProperty Include="RootNamespace" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Project>

</Project>
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;

using dotnetCampus.YamlToCSharp.Utils;

using Microsoft.CodeAnalysis;

namespace dotnetCampus.YamlToCSharp.Analyzers;

[Generator(LanguageNames.CSharp)]
public class YamlToCSharpIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//Debugger.Launch();
//Debugger.Break();
IncrementalValueProvider<Compilation> compilationProvider =
context.CompilationProvider.Select((compilation, token) => compilation);

// 先找到所有感兴趣的文件
var yamlFileProvider = context.AdditionalTextsProvider.Where(t =>
{
var extension = Path.GetExtension(t.Path);
return string.Equals(extension, ".yml", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".yaml", StringComparison.OrdinalIgnoreCase);
})
// 和配置合并,用来获取项目属性
.Combine(context.AnalyzerConfigOptionsProvider);
;

// 再进行处理,转换为代码。这一步能在底层提供缓存,减少重复转换
IncrementalValuesProvider<(string sourceFileName, string code)> csharpCodeProvider = yamlFileProvider.Select((args,token) =>
{
var (ymlText, analyzerConfigOptionsProvider) = args;

DirectoryInfo projectDirectory;

// 通过 csproj 等 PropertyGroup 里面获取
// 需要将可见的,放入到 CompilerVisibleProperty 里面
// 需要加上 `build_property.` 前缀
if (analyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MSBuildProjectDirectory",out var msBuildProjectDirectory))
{
projectDirectory = new DirectoryInfo(msBuildProjectDirectory);
}
else
{
// 应该不会找不到
projectDirectory = FindProjectDirectory(ymlText.Path);
}

if (!analyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace))
{
// 尝试去获取配置的命名空间,如果能获取到,那么就使用配置的
// 获取不到,采用默认的
rootNamespace = "dotnetCampus.Localizations";
}

var (classNamespace, className) = IdentifierHelper.MakeNamespaceAndClassName(projectDirectory,
new FileInfo(ymlText.Path), rootNamespace);

var sourceFileName = classNamespace + "." + className + ".yml" + ".cs";

var sourceText = ymlText.GetText(token);
if (sourceText != null)
{
TryLoadYamlDotNet();

var yamlText = sourceText.ToString();
var yamlFileToCSharpFile = new YamlFileToCSharpFile();
var code = yamlFileToCSharpFile.YamlToCsharpCode(yamlText, className, classNamespace,
needAddPartial: false);
return (sourceFileName, code);
}

return (sourceFileName, string.Empty);
});

// 全部收集起来,再加入源代码,用来解决重复加入
IncrementalValueProvider<ImmutableArray<(string sourceFileName, string code)>> yamlCodeListProvider = csharpCodeProvider.Collect();

var resultProvider = yamlCodeListProvider.Combine(compilationProvider);

// 添加到源代码
context.RegisterSourceOutput(resultProvider, (sourceProductionContext, provider) =>
{
var (yamlCodeList, compilation) = provider;

foreach (var (sourceFileName, code) in yamlCodeList)
{
if (!sourceProductionContext.CancellationToken.IsCancellationRequested)
{
sourceProductionContext.AddSource(sourceFileName, code);
}
}
});
}

private static void TryLoadYamlDotNet()
{
// 尝试加上 YamlDotNet.dll 文件,由于源代码生成没有拷贝依赖,需要手动加载
// [Analyzer not working using Nuget · Issue #56076 · dotnet/roslyn](https://github.com/dotnet/roslyn/issues/56076)
// [I can't use source generator with package reference · Issue #56024 · dotnet/roslyn](https://github.com/dotnet/roslyn/issues/56024)

// YamlDotNet, Version=11.0.0.0, Culture=neutral, PublicKeyToken=ec19458f3c15af5e
var yamlDotNetAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(t => t.FullName.Contains("YamlDotNet,"));
if (yamlDotNetAssembly != null)
{
// 加载成功
return;
}

var assembly = Assembly.GetExecutingAssembly();
var location = assembly.Location;
var directory = Path.GetDirectoryName(location)!;
var yamlDotNetStream = assembly.GetManifestResourceStream("dotnetCampus.YamlToCSharp.Analyzers.YamlDotNet.dll")!;
var yamlDotNetFile = Path.Combine(directory, "YamlDotNet.dll");
using (var fileStream = File.OpenWrite(yamlDotNetFile))
{
yamlDotNetStream.CopyTo(fileStream);
}

Assembly.LoadFrom(yamlDotNetFile);
}

private DirectoryInfo FindProjectDirectory(string fileName)
{
// 第一层的文件夹一定存在
var currentDirectory = Path.GetDirectoryName(fileName)!;

var directory = currentDirectory;
while (directory is not null)
{
if (Directory.GetFiles(directory, "*.csproj", SearchOption.TopDirectoryOnly).Any())
{
// 如果能找到 csproj 文件,那就是项目所在文件夹了
return new DirectoryInfo(directory);
}

directory = Path.GetDirectoryName(directory);
}

return new DirectoryInfo(currentDirectory);
}

private string FileNameToClassName(string fileName)
{
return fileName.Replace("-", "_").Replace(".", "_").Replace(" ", "_");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\dotnetCampus.YamlToCSharp.Build\Utils\IdentifierHelper.cs" Link="IdentifierHelper.cs" />
<Compile Include="..\dotnetCampus.YamlToCSharp.Build\Utils\PathHelper.cs" Link="PathHelper.cs" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\dotnetCampus.YamlToCSharp\**\*.cs" Exclude="..\dotnetCampus.YamlToCSharp\obj\**\*.cs;..\dotnetCampus.YamlToCSharp\bin\**\*.cs" Link="YamlToCSharp\%(RecursiveDir)\%(FileName).cs" />
<None Remove="YamlDotNet.dll" />
<EmbeddedResource Include="YamlDotNet.dll" />
<PackageReference Include="YamlDotNet" Version="11.2.1" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" />
</ItemGroup>

</Project>
Loading

0 comments on commit a883b5e

Please sign in to comment.