dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.09k stars 4.7k forks source link

我应该如何正确的使用 IIncrementalGenerator #108744

Open snltty opened 3 hours ago

snltty commented 3 hours ago

linker.gen

这是一个源生成器项目,里面使用了IIncrementalGenerator,我希望这个生成器能自动帮我查找一些接口有哪些实现,,然后为一些类自动添加 GetSourceGeneratorTypes 方法,获取这些实现

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <AnalyzerLanguage>cs</AnalyzerLanguage>
        <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
        <IsRoslynComponent>true</IsRoslynComponent>
        <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" PrivateAssets="all" />
        <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
    </ItemGroup>

</Project>
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using System.Linq;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Text;
using System.Text;

namespace linker.gen
{

    [Generator(LanguageNames.CSharp)]
    public class InterfaceSourceGenerator : IIncrementalGenerator
    {
        private List<GeneratorInfo> generators = new List<GeneratorInfo> {
             new GeneratorInfo{ ClassName="FlowTransfer", ClassNameSpace="linker.plugins.flow", InterfaceName="linker.plugins.flow.IFlow" },
             new GeneratorInfo{ ClassName="RelayValidatorTransfer", ClassNameSpace="linker.plugins.relay.validator", InterfaceName="linker.plugins.relay.validator.IRelayValidator" },
             new GeneratorInfo{ ClassName="SignInArgsTransfer", ClassNameSpace="linker.plugins.signIn.args", InterfaceName="linker.plugins.signIn.args.ISignInArgs" },
             new GeneratorInfo{ ClassName="RelayTransfer", ClassNameSpace="linker.plugins.relay", InterfaceName="linker.plugins.relay.transport.ITransport" },
             new GeneratorInfo{ ClassName="ResolverTransfer", ClassNameSpace="linker.plugins.resolver", InterfaceName="linker.plugins.resolver.IResolver" },
             new GeneratorInfo{ ClassName="TunnelExcludeIPTransfer", ClassNameSpace="linker.plugins.tunnel.excludeip", InterfaceName="linker.plugins.tunnel.excludeip.ITunnelExcludeIP" },
             new GeneratorInfo{ ClassName="StartupTransfer", ClassNameSpace="linker.startup", InterfaceName="linker.startup.IStartup", Instance=true },
             new GeneratorInfo{ ClassName="MessengerResolverTypes", ClassNameSpace="linker.plugins.messenger", InterfaceName="linker.plugins.messenger.IMessenger"},
             new GeneratorInfo{ ClassName="ApiClientServer", ClassNameSpace="linker.plugins.capi", InterfaceName="linker.plugins.capi.IApiClientController"},
        };

        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            IncrementalValueProvider<Compilation> compilations = context.CompilationProvider.Select((compilation, cancellationToken) => compilation);

            context.RegisterSourceOutput(compilations, (sourceProductionContext, compilation) =>
            {
                foreach (GeneratorInfo info in generators)
                {
                    var iFlowSymbol = compilation.GetTypeByMetadataName(info.InterfaceName);
                    List<string> types = new List<string> { };
                    List<string> classs = new List<string> { };
                    List<string> namespaces = new List<string> { };

                    foreach (var syntaxTree in compilation.SyntaxTrees)
                    {
                        if (syntaxTree == null)
                        {
                            continue;
                        }
                        var root = syntaxTree.GetRoot(sourceProductionContext.CancellationToken);
                        var classDeclarationSyntaxs = root
                            .DescendantNodes(descendIntoTrivia: false)
                            .OfType<ClassDeclarationSyntax>();

                        foreach (var classDeclarationSyntax in classDeclarationSyntaxs)
                        {
                            var model = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree);
                            var classSymbol = model.GetDeclaredSymbol(classDeclarationSyntax) as INamedTypeSymbol;
                            if (classSymbol.AllInterfaces.Contains(iFlowSymbol))
                            {
                                types.Add($"typeof({classDeclarationSyntax.Identifier.Text})");

                                if (info.Instance)
                                    classs.Add($"new {classDeclarationSyntax.Identifier.Text}()");

                                var namespaceDecl = classDeclarationSyntax.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
                                if (namespaceDecl != null)
                                {
                                    namespaces.Add($"using {namespaceDecl.Name.ToString()};");
                                }

                            }

                        }
                    }

                    var source = $@"
                    using System;
                    using System.Collections.Generic;
                    {string.Join("\r\n", namespaces)}

                    namespace {info.ClassNameSpace}
                    {{
                        public partial class {info.ClassName}
                        {{
                             public static List<Type> GetSourceGeneratorTypes()
                             {{
                                return new List<Type> {{
                                    {string.Join(",", types)}
                                }};
                             }}
                             public static List<{info.InterfaceName}> GetSourceGeneratorInstances()
                             {{
                                return new List<{info.InterfaceName}> {{
                                    {string.Join(",", classs)}
                                }};
                             }}
                        }}
                    }}";

                    var sourceText = SourceText.From(source, Encoding.UTF8);
                    sourceProductionContext.AddSource($"{info.ClassName}Instances.g.cs", sourceText);
                }

            });
        }

        public sealed class GeneratorInfo
        {
            public string ClassName { get; set; }
            public string ClassNameSpace { get; set; }
            public string InterfaceName { get; set; }
            public bool Instance { get; set; }
        }
    }
}

linker

然后我在linker项目中引用 linker.gen

<PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net8.0</TargetFrameworks>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>disable</Nullable>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
    <Configurations>Debug;Release</Configurations>
    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
    <PublishAot>false</PublishAot>
    <JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
    <SignAssembly>true</SignAssembly>
    <EnablePreviewFeatures>true</EnablePreviewFeatures>
    <ServerGarbageCollection>false</ServerGarbageCollection>
</PropertyGroup>
<ItemGroup>
    <ProjectReference Include="..\linker.gen\linker.gen.csproj">
        <OutputItemType>Analyzer</OutputItemType>
        <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
    </ProjectReference>
</ItemGroup>

当我在DEBUG下时,这正常使用,满足的我需求,当我需要发布 linker项目,并且启用裁剪时,会发布失败

严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 修整程序集需要 .NET Core 3.0 或更高版本。 linker C:\Program Files\dotnet\sdk\8.0.303\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets 90

huoyaoyuan commented 3 hours ago

Source generator related questions belong to https://github.com/dotnet/roslyn/discussions .

How are you publishing with trim enabled? You should set PublishTrimmed property in the entry application or command line, not in the source generator project.

snltty commented 2 hours ago

如上面的代码,源生成器项目linker.gen中没有设置PublishTrimmed ,是linker项目引用了linker.gen,使用dotnet publish 发布linker项目。指定了裁剪