[英]How can I make an incremental source generator run in Visual Studio?
我已经按照文档创建了一个基本的增量源代码生成器,它输出一个标有属性的类的副本:
// Code found in my project
[Copy]
public class MyClass
{
public int Value { get; set; }
}
// Output from the source generator
public class Copy_MyClass
{
public int Value { get; set; }
}
源代码生成本身工作得很好,但我在将它与 Visual Studio 2022 一起使用时遇到了两个问题:
例如,如果我使用 [Copy] 属性创建一个新的 class Foo
,然后尝试在我的代码中引用Copy_Foo
,Visual Studio 将显示Copy_Foo
未定义的错误。
[Copy]
public class Foo { }
public static class TestCopy
{
public static void Test()
{
Copy_Foo foo = new Copy_Foo();
// ^^^^^^^^
// The type or namespace 'Copy_Foo' could not be found
}
}
构建此代码会成功,但 Visual Studio 将继续认为Copy_Foo
不存在。 一旦我重新启动,它就会知道Copy_Foo
存在,但在我再次重新启动之前不会对它进行任何更改。
我创建了一个小示例,它只输出 [Copy] 属性。
第一个项目包含源代码生成器:
// TestSourceGenerator.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
// TestSourceGenerator.cs
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace TestSourceGenerator
{
[Generator]
public class TestSourceGenerator : IIncrementalGenerator
{
private const string CopyAttributeSource = @"
namespace Generated
{
public class CopyAttribute : System.Attribute { }
}";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(ctx => ctx.AddSource("CopyAttribute.g.cs", SourceText.From(CopyAttributeSource, Encoding.UTF8)));
}
}
}
第二个消耗生成器:
// TestSourceGenerator.Test.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TestSourceGenerator\TestSourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
// MyClass.cs
namespace TestSourceGenerator.Test
{
[Generated.CopyAttribute]
public class MyClass
{
}
}
如果您创建没有ProjectReference
的 TestSourceGenerator.Test 项目,在 Visual Studio 中打开它,然后编辑项目文件以引用分析器,您将看到 Visual Studio 在[Generated.CopyAttribute]
上显示错误,但项目构建成功地。
有没有办法让 Visual Studio 在 IDE 中运行我的源代码生成器并从生成的代码中获取符号? 根据文档,这似乎应该是一个受支持的用例,甚至是增量源代码生成器存在的主要动机之一。
您的挣扎通常有两个原因,如果没有很好地理解,这两个原因都会令人沮丧。
一个问题是 Visual Studio 不允许分析器程序集在加载后卸载。 一旦 Visual Studio 加载了用于IDE 和 Intellisense的分析器,它将继续使用该版本,直到您关闭 Visual Studio,或者至少直到您增加程序集版本。 但是,当您为项目点击构建/重建时,Visual Studio 将生成一个新的msbuild进程,该进程(通常)将加载分析器的新版本。 因此,您最终可能会得到一个构建良好但不更新 IDE 和 Intellisense 的项目。 这实际上是一个老问题,与生成器没有直接关系,请参阅https://github.com/do.net/roslyn/issues/48083了解更多信息。 (一个建议是不要通过 Visual Studio 而是在单元测试的帮助下“实时”开发分析器。)
另一个缓存问题涉及使用IIncrementalGenerator
进行增量构建,但我不确定这是否与您发布的代码相关。 无论如何,如果您使用得当,这个更新版本的源代码生成器将缓存最后一次执行,并在相关内容没有发生变化的情况下将 output 重新用于 IDE/Intellisense 。 这通常需要您为源语法节点的内容实现自定义相等比较器。 然而,如果这种比较没有考虑到相关内容(即实际随着最后一次按键而改变的内容),生成器将不会被执行,IDE/Intellisense 也不会被更新。 同样, msbuild可能仍然运行良好,因为每个新构建都会忽略任何以前的 output 缓存,并且从一开始就为分析器提供每个源节点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.