IIncrementalGenerator 增量 Source Generator 生成代码入门 读取 csproj 项目文件的属性配置
本文告诉大家如何在使用 IIncrementalGenerator 进行增量的 Source Generator 生成代码时,读取项目里的项目文件属性,从而实现为项目定制的逻辑。或者是读取 NuGet 包里面的一些配置,从而方便实现逻辑
使用增量的源代码生成具有更高的门槛。本文属于入门博客,但非编程新手友好,期望阅读本文之前,已了解源代码生成和项目构建和项目组织的基础知识
阅读本文,你可以了解到如何在进行增量的源代码生成过程中,读取项目文件里面的属性,从而执行特殊的逻辑
本文的例子期望达成的是,读取 csproj 项目文件里面的 MyCustomProperty 属性,将此属性的文本内容,作为生成代码的一部分。以下代码是 MyCustomProperty 属性的定义。值得一说的是,此方法不仅仅适合用在读取 csproj 项目文件里面的属性,也适合用来读取 NuGet 包的 xx.props 和 xx.targets 文件里面的属性
<PropertyGroup>
<MyCustomProperty>lindexi is doubi</MyCustomProperty>
</PropertyGroup>
在例子代码里面,期望能够将 MyCustomProperty 属性的内容,作为控制台输出的参数,输出到。相当于将 MyCustomProperty 属性的内容,放入到下面代码的 text 变量里面,加入到源代码生成
var code = @"using System;
namespace LainewihereJerejawwerye
{
public static class Foo
{
public static void F1()
{
Console.WriteLine(""" + text + @""");
}
}
}";
接下来是开始写这个例子的代码,本文的所有代码都可以在本文末尾找到下载地址
开始之前,按照惯例,先新建两个项目,分别是 LainewihereJerejawwerye 和 LainewihereJerejawwerye.Analyzers 两个项目。其中 LainewihereJerejawwerye 用来安装使用分析器的项目,提供 MyCustomProperty 属性。在 LainewihereJerejawwerye.Analyzers 里面,作为分析器项目,将实现源代码生成逻辑
编辑 LainewihereJerejawwerye.Analyzers 的 csproj 项目文件,替换为以下代码。下面代码的细节请参阅 使用 Source Generator 在编译你的 .NET 项目时自动生成代码 - walterlv 博客
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<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>
接着编辑 LainewihereJerejawwerye 项目的 csproj 项目文件,让他引用上分析器项目。额外的加上 MyCustomProperty 属性,修改之后的代码如下
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<MyCustomProperty>lindexi is doubi</MyCustomProperty>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LainewihereJerejawwerye.Analyzers\LainewihereJerejawwerye.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
根据官方的 Source Generators Cookbook 文档,想要让分析器项目能够拿到 csproj 项目文件里面的属性,就需要明确使用 CompilerVisibleProperty 包含其对分析器可见的属性名。在属性系统里面,可以分为全局属性以及单项属性。所谓全局属性,就是对整个项目可用,而不是对项目里的某个文件进行设置的属性。单项属性就是对单个项,如单个文件进行设置的额外的配置属性。本文这里只讨论全局的属性配置情况,也就是对整个项目的配置的属性
如上文描述,添加一个 CompilerVisibleProperty 包含对分析器可见的 MyCustomProperty 属性,代码如下
<ItemGroup>
<CompilerVisibleProperty Include="MyCustomProperty" />
</ItemGroup>
加上 CompilerVisibleProperty 之后,分析器才可以通过 GlobalOptions 获取属性。获取时,需要分析器项目使用 TryGetValue 方法,且要求在属性前面加上 build_property. 前缀。下文的例子将会告诉大家具体的获取方法
这里还存在一个问题,那就是属性的时机,如果属性的赋值是在分析器执行完成之后再赋值,那自然会让分析器拿不到符合预期的属性内容。而如果属性过早赋值,可能属性本身的逻辑无法实现。因此需要找到一个最迟的时机,这是在分析器可以获取到属性内容的最后时机,如以下代码,可以放在 GenerateMSBuildEditorConfigFileCore 执行之前
<Target Name="Xxxxxxxx" BeforeTargets="GenerateMSBuildEditorConfigFileCore">
<PropertyGroup>
<MyCustomProperty>xxxxx</MyCustomProperty>
</PropertyGroup>
</Target>
如果属性能够一开始就赋值,那推荐就是一开始就赋值。如果属性有其他依赖,那推荐使用类似上面代码的写法。如果属性需要在 GenerateMSBuildEditorConfigFileCore 才获取到内容的,那就凉凉了,需要修改实现
完成配置之后,开始编写分析器项目的代码,由于分析器项目采用的是增量源代码构建,逻辑上会比较复杂一些。在增量源代码生成里面,是没有直接提供 GlobalOptions 用来访问的,而是需要按照增量的方法,先过滤出感兴趣的内容。在感兴趣的内容发生变更或初始化时,将会触发实际执行的逻辑,在实际执行的逻辑,通过过滤条件的输出结果,拿到参数,生成代码
先开始搭建基础的代码,在 LainewihereJerejawwerye.Analyzers 新建一个叫 CodeCollectionIncrementalGenerator 的类型,此类将用来编写本文的核心代码。类名随意,可以自己修改
using System;
using Microsoft.CodeAnalysis;
namespace LainewihereJerejawwerye.Analyzers
{
[Generator(LanguageNames.CSharp)]
public class CodeCollectionIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 在这里编写代码
}
}
}
使用增量代码生成,可以看到继承的是 IIncrementalGenerator 接口,需要实现的只有初始化函数,而不是一个初始化和一个执行函数。在增量代码生成里,需要在此初始化函数里面完成所有代码逻辑。但不代表着就是在初始化函数里面执行完成,因为实际上在此初始化函数里面,更多的是注入各个委托,在各个委托里面实现逻辑。在编写代码过程中,各个委托将会按需被调度执行,从而完成增量代码生成
按照增量代码生成的编写要求,第一步是声明对什么感兴趣,也就是一次过滤。只有满足条件的内容发生变更或初始化时,才会触发后续逻辑,同时过滤的结果也会作为后续逻辑的输入参数。本文这里需要的只是配置属性而已。配置属性都放在 AnalyzerConfigOptionsProvider 里,换句话说,我可以对整个 AnalyzerConfigOptionsProvider 都感兴趣。于是 AnalyzerConfigOptionsProvider 属性就是我的过滤条件
于是将 AnalyzerConfigOptionsProvider 作为参数条件传入到 RegisterImplementationSourceOutput 方法里面,也就是只有在配置初始化或变更时,才会触发传入 RegisterImplementationSourceOutput 的委托
context.RegisterImplementationSourceOutput(context.AnalyzerConfigOptionsProvider,
(productionContext, provider) =>
{
// 这里的代码只有当配置初始化或变更时才会被执行
};
这里拿到的 provider 就是项目的配置了,其中本文期望的 csproj 项目文件的属性也就在 GlobalOptions 属性里面,可以通过如下代码进行获取
context.RegisterImplementationSourceOutput(context.AnalyzerConfigOptionsProvider,
(productionContext, provider) =>
{
var text = string.Empty;
// 通过 csproj 等 PropertyGroup 里面获取
// 需要将可见的,放入到 CompilerVisibleProperty 里面
// 需要加上 `build_property.` 前缀
if (provider.GlobalOptions.TryGetValue("build_property.MyCustomProperty", out var myCustomProperty))
{
text += " " + myCustomProperty;
}
};
如此即可拿到属性的内容,放入到 text 变量。接着再使用本文已开始的生成代码,完成之后的代码如下
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterImplementationSourceOutput(context.AnalyzerConfigOptionsProvider,
(productionContext, provider) =>
{
var text = string.Empty;
// 通过 csproj 等 PropertyGroup 里面获取
// 需要将可见的,放入到 CompilerVisibleProperty 里面
// 需要加上 `build_property.` 前缀
if (provider.GlobalOptions.TryGetValue("build_property.MyCustomProperty", out var myCustomProperty))
{
text += " " + myCustomProperty;
}
var code = @"using System;
namespace LainewihereJerejawwerye
{
public static class Foo
{
public static void F1()
{
Console.WriteLine(""" + text + @""");
}
}
}";
productionContext.AddSource("Demo", code);
});
}
尝试在 LainewihereJerejawwerye 项目调用一下
Foo.F1();
然后运行 LainewihereJerejawwerye 项目,可以看到输出了 MyCustomProperty 属性的内容,证明获取 csproj 项目文件里的属性成功
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin d4fee9a801c5d2591162ce4280ac06906029ef48
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin d4fee9a801c5d2591162ce4280ac06906029ef48
获取代码之后,进入 LainewihereJerejawwerye 文件夹
更多源代码生成,请看官方的 Source Generators Cookbook
更多关于我博客请参阅 博客导航
IIncrementalGenerator 增量 Source Generator 生成代码入门 读取 csproj 项目文件的属性配置的更多相关文章
- mybatis Generator生成代码及使用方式
本文原创,转载请注明:http://www.cnblogs.com/fengzheng/p/5889312.html 为什么要有mybatis mybatis 是一个 Java 的 ORM 框架,OR ...
- mybatis之generator生成代码
首先在pom文件中引入以下代码 <plugin> <groupId>org.mybatis.generator</groupId> <artifactId&g ...
- springboot快速入门(二)——项目属性配置(日志详解)
一.概述 application.properties就是springboot的属性配置文件 在使用spring boot过程中,可以发现项目中只需要极少的配置就能完成相应的功能,这归功于spring ...
- 用org.mybatis.generator 生成代码
1:引入pom 2:增加生成配置xml: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ...
- 2016.7.12 eclispe使用mybatis generator生成代码时提示project E is not exist
运行mybatis-generator之后,出现错误:project E is not exist 错误原因:使用了项目的绝对路径. http://bbs.csdn.net/topics/3914 ...
- mybatis Generator生成代码及使用方式(转载)
转载自:http://www.cnblogs.com/fengzheng/p/5889312.html 文章很棒,很不错,转了.
- 把Mybatis Generator生成的代码加上想要的注释
作者:王建乐 1 前言 在日常开发工作中,我们经常用Mybatis Generator根据表结构生成对应的实体类和Mapper文件.但是Mybatis Generator默认生成的代码中,注释并不是我 ...
- IDEA使用mybatis generator自动生成代码
主要就三步: 1.pom 文件中引入jar包并配置 build 属性 <dependencies> <!-- 自动生产mapper Begin! --> <depende ...
- springboot 使用mybatis-generator自动生成代码
这里只介绍mybatis generator生成代码 一.pom配置 在build-->plugins-->添加plugin <plugin> <groupId>o ...
- Java 读取application.properties配置文件中配置
实际开发中若需要读取配置文件application.properties中的配置,代码如下.例:读取配置文件中name属性配置值: 代码如下: import org.springframework.c ...
随机推荐
- python面向对象(绑定方法与非绑定方法)
# 一.绑定方法:特殊之处在于将调用者本身当做第一个参数自动传入 # 1)绑定给对象的方法:调用者是对象,自动传入的是对象 # 2)绑定给类的方法:调用者是类,自动传入的是类 # 类方法的应用 # 需 ...
- 记录--谁还没个靠bug才能正常运行的程序
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 最近遇到一个问题,计算滚动距离,滚动比例达到某界定值时,显示mask,很常见吧^ _ ^ 这里讲的不是这个需求的实现,是其中遇到了一个比较 ...
- CLion和WSL配置MPI运行及调试环境
本文将介绍 Windows 下,使用 CLion 和 WSL 配置 MPI 运行及调试环境的方法. 0. 前提 阅读本文前,请确保: Windows 下已启用 WSL2,并安装了任一 Linux 发行 ...
- KingbaseES V8R6 sslinfo 插件
前言 KingbaseES对使用SSL 连接加密客户端/服务器通讯的本地支持,可以增加数据传输安全性. 本文展示配置ssl连接,并通过安装一个插件验证ssl加密认证使用. 一.配置ssl连接过程: s ...
- #交互,分类讨论#CF1292E Rin and The Unknown Flower
题目传送门 分析 先尝试锁定一个字母,显然询问 \(CH,CO,CC\) 会比直接询问 \(C\) 更优,虽然牺牲了最后一个位置是否为 \(C\) 的查询. 同理,询问 \(HH,OH,CH\) 会比 ...
- #莫比乌斯反演,期望#CF1139D Steps to One
题目 每次随机选一个 \(1\) 到 \(m\) 之间的数加在数列末尾, 数列中所有数的 \(\gcd=1\) 时停止,求数列期望长度.\(m\leq 10^5\) 分析 求期望长度的一种方法就是枚举 ...
- #min-max容斥#51nod 1355 斐波那契的最小公倍数
题目 对于 \(n\leq 50000,a_i\leq 10^6\),求 \(\large lcm(fib(a_1),fib(a_2),\dots,fib(a_{n-1}),fib(a_n))\) 分 ...
- #模型转换#[ARC126C] Maximize GCD
题目 有 \(n\) 个数,最多 \(k\) 次让所选择的数加一,求 \(n\) 个数的GCD的最大值 \(n,a_i\leq 3*10^5,k\leq 10^{18}\) 分析 设答案为 \(d\) ...
- #随机#CF1198F GCD Groups 2
题目 将 \(n\) 个数分为两组,使得两组的GCD都为1,求具体的分组情况 分析 考虑直接打乱 \(n\) 个数,如果能使第一组GCD减小就减小,否则丢到第二组, 由于打乱后出错的概率会减小,所以r ...
- 【直播回顾】OpenHarmony知识赋能六期第三课—OpenHarmony智能家居项目之控制面板功能实现
7月14日晚上19点,知识赋能第六期第三节直播 <OpenHarmony智能家居项目之控制面板功能实现> ,在OpenHarmony开发者成长计划社群内成功举行. 本次直播是"O ...