From 763b460575e00e471c62eec95b5f7633313b8303 Mon Sep 17 00:00:00 2001 From: GwWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Thu, 25 Dec 2025 21:10:17 +0800 Subject: [PATCH] =?UTF-8?q?refactor(GFramework.Generator):=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90=E5=99=A8=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E5=B9=B6=E6=B7=BB=E5=8A=A0Godot=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除原有的LoggerGenerator.cs和相关README文档 - 重命名GFramework.Generator为GFramework.SourceGenerators - 重命名GFramework.Generator.Attributes为GFramework.SourceGenerators.Attributes - 添加新的Godot日志生成器(GodotLoggerGenerator)及对应属性(GodotLogAttribute) - 创建GFramework.Godot.SourceGenerators新项目用于Godot特定功能 - 修改日志生成器使用GodotLoggerFactory而非ConsoleLoggerFactory --- GFramework.Generator/README.md | 4 - .../generator/logging/LoggerGenerator.cs | 185 -------- .../generator/logging/README.md | 443 ------------------ ...k.Godot.SourceGenerators.Attributes.csproj | 9 + .../logging/GodotLogAttribute.cs | 37 ++ .../AnalyzerReleases.Shipped.md | 0 .../AnalyzerReleases.Unshipped.md | 9 + .../GFramework.Godot.SourceGenerators.csproj | 19 + .../logging/Diagnostic.cs | 44 ++ .../logging/GodotLoggerGenerator.cs | 181 +++++++ ...amework.SourceGenerators.Attributes.csproj | 0 .../enums/EnumExtensionsAttribute.cs | 2 +- .../logging/LogAttribute.cs | 24 +- .../AnalyzerReleases.Shipped.md | 3 + .../AnalyzerReleases.Unshipped.md | 0 .../GFramework.SourceGenerators.csproj | 10 +- .../enums/EnumExtensionsGenerator.cs | 5 +- .../logging/Diagnostic.cs | 16 +- .../logging/LoggerGenerator.cs | 185 ++++++++ GFramework.csproj | 45 +- GFramework.sln | 16 +- 21 files changed, 568 insertions(+), 669 deletions(-) delete mode 100644 GFramework.Generator/README.md delete mode 100644 GFramework.Generator/generator/logging/LoggerGenerator.cs delete mode 100644 GFramework.Generator/generator/logging/README.md create mode 100644 GFramework.Godot.SourceGenerators.Attributes/GFramework.Godot.SourceGenerators.Attributes.csproj create mode 100644 GFramework.Godot.SourceGenerators.Attributes/logging/GodotLogAttribute.cs rename {GFramework.Generator => GFramework.Godot.SourceGenerators}/AnalyzerReleases.Shipped.md (100%) create mode 100644 GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md create mode 100644 GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj create mode 100644 GFramework.Godot.SourceGenerators/logging/Diagnostic.cs create mode 100644 GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs rename GFramework.Generator.Attributes/GFramework.Generator.Attributes.csproj => GFramework.SourceGenerators.Attributes/GFramework.SourceGenerators.Attributes.csproj (100%) rename {GFramework.Generator.Attributes/generator => GFramework.SourceGenerators.Attributes}/enums/EnumExtensionsAttribute.cs (92%) rename {GFramework.Generator.Attributes/generator => GFramework.SourceGenerators.Attributes}/logging/LogAttribute.cs (85%) create mode 100644 GFramework.SourceGenerators/AnalyzerReleases.Shipped.md rename {GFramework.Generator => GFramework.SourceGenerators}/AnalyzerReleases.Unshipped.md (100%) rename GFramework.Generator/GFramework.Generator.csproj => GFramework.SourceGenerators/GFramework.SourceGenerators.csproj (83%) rename {GFramework.Generator/generator => GFramework.SourceGenerators}/enums/EnumExtensionsGenerator.cs (96%) rename {GFramework.Generator/generator => GFramework.SourceGenerators}/logging/Diagnostic.cs (79%) create mode 100644 GFramework.SourceGenerators/logging/LoggerGenerator.cs diff --git a/GFramework.Generator/README.md b/GFramework.Generator/README.md deleted file mode 100644 index 08b71e1..0000000 --- a/GFramework.Generator/README.md +++ /dev/null @@ -1,4 +0,0 @@ -项目额外的简单代码生成器 -目前已有的功能 - -- 为枚举添加两个扩展方法方便判断枚举值 \ No newline at end of file diff --git a/GFramework.Generator/generator/logging/LoggerGenerator.cs b/GFramework.Generator/generator/logging/LoggerGenerator.cs deleted file mode 100644 index 766558f..0000000 --- a/GFramework.Generator/generator/logging/LoggerGenerator.cs +++ /dev/null @@ -1,185 +0,0 @@ -#nullable enable -using System; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - -namespace GFramework.Generator.generator.logging -{ - /// - /// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段 - /// - [Generator] - public sealed class LoggerGenerator : IIncrementalGenerator - { - // 请确保这里的命名空间和类名与 Attributes 项目中定义的完全一致(注意大小写!) - private const string AttributeMetadataName = "GFramework.Generator.Attributes.generator.logging.LogAttribute"; - private const string AttributeShortName = "LogAttribute"; - private const string AttributeShortNameWithoutSuffix = "Log"; - - /// - /// 初始化生成器,设置语法过滤和代码生成逻辑 - /// - /// 增量生成器初始化上下文 - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // 1. 语法过滤:快速筛选候选类 - var targets = context.SyntaxProvider.CreateSyntaxProvider( - static (node, _) => - { - if (node is not ClassDeclarationSyntax cls) return false; - // 只要包含 Log 字眼的 Attribute 就先放行 - return cls.AttributeLists.SelectMany(a => a.Attributes).Any(a => - { - var name = a.Name.ToString(); - // 简单的字符串匹配,防止错过别名情况 - return name.Contains(AttributeShortNameWithoutSuffix); - }); - }, - static (ctx, _) => - { - var classDecl = (ClassDeclarationSyntax)ctx.Node; - var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl); - return (ClassDecl: classDecl, Symbol: symbol); - }) - .Where(x => x.Symbol is not null); - - // 2. 生成代码 - context.RegisterSourceOutput(targets, (spc, pair) => - { - try - { - var classDecl = pair.ClassDecl; - var classSymbol = pair.Symbol!; - - // 再次确认是否真的含有目标 Attribute (语义检查) - var attr = GetAttribute(classSymbol); - if (attr == null) return; // 可能是名字相似但不是我们要的 Attribute - - // 检查 partial - if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword)) - { - spc.ReportDiagnostic(Diagnostic.Create( - Diagnostics.MustBePartial, - classDecl.Identifier.GetLocation(), - classSymbol.Name)); - - return; - } - - var source = Generate(classSymbol, attr); - var hintName = $"{classSymbol.Name}.Logger.g.cs"; - spc.AddSource(hintName, SourceText.From(source, Encoding.UTF8)); - } - catch (Exception ex) - { - // === 关键修复:生成错误报告文件 === - var errorSource = $"// source generator error: {ex.Message}\n// StackTrace:\n// {ex.StackTrace}"; - // 替换非法字符以防文件名报错 - var safeName = pair.Symbol?.Name ?? "Unknown"; - spc.AddSource($"{safeName}.Logger.Error.g.cs", SourceText.From(errorSource, Encoding.UTF8)); - } - }); - } - - /// - /// 获取类符号上的LogAttribute特性 - /// - /// 类符号 - /// LogAttribute特性数据,如果不存在则返回null - private static AttributeData? GetAttribute(INamedTypeSymbol classSymbol) - { - return classSymbol.GetAttributes().FirstOrDefault(a => - { - var cls = a.AttributeClass; - if (cls == null) return false; - - // 宽松匹配:全名匹配 OR 名字匹配 - return cls.ToDisplayString() == AttributeMetadataName || - cls.Name == AttributeShortName; - }); - } - - /// - /// 生成日志字段代码 - /// - /// 类符号 - /// LogAttribute特性数据 - /// 生成的C#代码字符串 - private static string Generate(INamedTypeSymbol classSymbol, AttributeData attr) - { - var ns = classSymbol.ContainingNamespace.IsGlobalNamespace - ? null - : classSymbol.ContainingNamespace.ToDisplayString(); - - var className = classSymbol.Name; - - // === 解析 Name === - var name = className; // 默认使用类名 - - // 检查是否有构造函数参数 - if (attr.ConstructorArguments.Length > 0) - { - var argValue = attr.ConstructorArguments[0].Value; - - name = argValue switch - { - // 情况 1: 参数存在,但值为 null (例如 [Log] 且构造函数有默认值 null) - null => className, - // 情况 2: 参数存在,且是有效的字符串 (例如 [Log("MyCategory")]) - string s when !string.IsNullOrWhiteSpace(s) => s, - _ => $"{className}_InvalidArg" - }; - } - - // === 解析 Named Arguments (更加安全的获取方式) === - var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log"; - var access = - GetNamedArg(attr, "AccessModifier")?.ToString() ?? - "private"; // 注意:如果你的 AccessModifier 是枚举,这里得到的可能是 int 或枚举名 - - // 处理 bool 类型 - var isStaticObj = GetNamedArg(attr, "IsStatic"); - var isStatic = isStaticObj is not bool b || b; // 默认为 true - - var staticKeyword = isStatic ? "static " : ""; - - var sb = new StringBuilder(); - sb.AppendLine("// "); - sb.AppendLine("using GFramework.Core.logging;"); // 确保这里引用了 ILog 和 Log 类 - - if (ns is not null) - { - sb.AppendLine($"namespace {ns}"); - sb.AppendLine("{"); - } - - sb.AppendLine($" public partial class {className}"); - sb.AppendLine(" {"); - sb.AppendLine($" /// Auto-generated logger"); - sb.AppendLine( - $" {access} {staticKeyword}readonly ILogger {fieldName} = " + - $"new ConsoleLoggerFactory.GetLogger(\"{name}\");"); - sb.AppendLine(" }"); - - if (ns is not null) - sb.AppendLine("}"); - - return sb.ToString(); - } - - /// - /// 从特性数据中获取命名参数的值 - /// - /// 特性数据 - /// 参数名称 - /// 参数值,如果不存在则返回null - private static object? GetNamedArg(AttributeData attr, string name) - { - return (from kv in attr.NamedArguments where kv.Key == name select kv.Value.Value).FirstOrDefault(); - } - } -} diff --git a/GFramework.Generator/generator/logging/README.md b/GFramework.Generator/generator/logging/README.md deleted file mode 100644 index 2ff3880..0000000 --- a/GFramework.Generator/generator/logging/README.md +++ /dev/null @@ -1,443 +0,0 @@ -# GFramework 日志代码生成器 (LogAttribute) - -## 概述 - -GFramework 提供了一个强大的日志代码生成器,类似于 Java 的 `@Slf4j` 注解。通过在类上使用 `[Log]` 特性,编译器会自动为该类生成一个日志记录器字段,让您在类的任何地方都能方便地使用日志记录功能。 - -## 快速开始 - -### 1. 基本使用 - -在类上添加 `[Log]` 特性: - -```csharp -using GFramework.Generator.Attributes.generator.logging; - -[Log] -public partial class MyService -{ - public void DoSomething() - { - // 自动生成的 Log 字段可以直接使用 - Log.Info("开始执行操作"); - - try - { - // 业务逻辑 - Log.Debug("执行业务逻辑", new { Operation = "DoSomething" }); - Log.Info("操作执行成功"); - } - catch (Exception ex) - { - Log.Error("操作执行失败", ex); - } - } -} -``` - -编译后,生成器会自动为该类添加: - -```csharp -public partial class MyService -{ - private static ILog Log = Log.CreateLogger("MyService"); -} -``` - -### 2. 自定义类别名称 - -```csharp -[Log("CustomService")] -public partial class MyService -{ - // 生成的 logger 类别为 "CustomService" 而不是 "MyService" -} -``` - -### 3. 自定义字段配置 - -```csharp -[Log(FieldName = "Logger", AccessModifier = "protected", IsStatic = false)] -public partial class MyService -{ - // 生成: protected ILog Logger = Log.CreateLogger("MyService"); -} -``` - -## 特性参数说明 - -### LogAttribute 构造函数参数 - -- **category** (string, 可选): 指定日志类别,默认为类名 - -### 命名参数 - -- **FieldName** (string, 默认 "Log"): 指定生成的字段名称 -- **AccessModifier** (string, 默认 "private"): 指定字段的访问修饰符 -- **IsStatic** (bool, 默认 true): 指定字段是否为静态的 - -## 使用示例 - -### 在系统中的使用 - -```csharp -[Log("System")] -public partial class GameSystem : AbstractSystem -{ - protected override void OnInit() - { - Log.Info("GameSystem 初始化开始"); - - // 初始化逻辑 - Log.Debug("正在加载游戏数据..."); - Log.Info("GameSystem 初始化完成"); - } - - protected override void OnUpdate(float deltaTime) - { - Log.Trace("系统更新", new { DeltaTime = deltaTime }); - } - - protected override void OnDestroy() - { - Log.Info("GameSystem 销毁"); - } -} -``` - -### 在模型中的使用 - -```csharp -[Log("Model")] -public partial class UserModel : AbstractModel -{ - public string UserName { get; set; } - - public void SetUserName(string userName) - { - Log.Debug("设置用户名", new { OldValue = UserName, NewValue = userName }); - UserName = userName; - Log.Info("用户名设置完成", new { UserName = userName }); - } -} -``` - -### 在工具类中的使用 - -```csharp -[Log("Utility")] -public partial class FileUtility -{ - public void SaveFile(string filePath, string content) - { - Log.Debug("开始保存文件", new { FilePath = filePath }); - - try - { - // 文件保存逻辑 - Log.Info("文件保存成功", new { FilePath = filePath }); - } - catch (Exception ex) - { - Log.Error("文件保存失败", ex, new { FilePath = filePath }); - } - } -} -``` - -### 网络服务中的使用 - -```csharp -[Log("Network")] -public partial class NetworkService -{ - public async Task GetDataAsync(string url) - { - Log.Debug("发起网络请求", new { Url = url, Method = "GET" }); - - try - { - var response = await httpClient.GetStringAsync(url); - Log.Info("网络请求成功", new { Url = url, ResponseLength = response.Length }); - return response; - } - catch (Exception ex) - { - Log.Error("网络请求失败", ex, new { Url = url }); - throw; - } - } -} -``` - -### 数据库服务中的使用 - -```csharp -[Log("Database")] -public partial class DatabaseService -{ - public async Task GetUserAsync(int userId) - { - Log.Debug("查询用户信息", new { UserId = userId }); - - try - { - var user = await _dbContext.Users.FindAsync(userId); - if (user != null) - { - Log.Info("用户查询成功", new { UserId = userId, UserName = user.Name }); - } - else - { - Log.Warn("用户不存在", new { UserId = userId }); - } - return user; - } - catch (Exception ex) - { - Log.Error("用户查询失败", ex, new { UserId = userId }); - throw; - } - } -} -``` - -## 高级配置示例 - -### 使用静态字段 - -```csharp -[Log(IsStatic = true)] // 默认配置 -public partial class StaticService -{ - public static void StaticMethod() - { - Log.Info("静态方法调用"); - } -} -``` - -### 使用实例字段 - -```csharp -[Log(IsStatic = false)] -public partial class InstanceService -{ - private readonly string _instanceId; - - public InstanceService(string instanceId) - { - _instanceId = instanceId; - } - - public void InstanceMethod() - { - Log.Info("实例方法调用", new { InstanceId = _instanceId }); - } -} -``` - -### 使用受保护的字段 - -```csharp -[Log(AccessModifier = "protected")] -public partial class BaseService -{ - // 子类可以访问 protected 字段 -} - -[Log] // 派生类也可以有自己的日志记录器 -public partial class DerivedService : BaseService -{ - public void DerivedMethod() - { - Log.Info("派生类方法"); - // 也可以访问基类的 protected Log 字段 - } -} -``` - -## 最佳实践 - -### 1. 使用合适的日志类别名称 - -```csharp -// 好的做法:使用有意义的类别名称 -[Log("UserService")] -public partial class UserService { } - -// 避免:使用过于通用的类别名称 -[Log("Service")] -public partial class UserService { } -``` - -### 2. 合理设置日志级别 - -```csharp -[Log("BusinessLogic")] -public partial class BusinessService -{ - public void ProcessOrder(Order order) - { - // 使用 Info 记录重要的业务流程 - Log.Info("开始处理订单", new { OrderId = order.Id }); - - // 使用 Debug 记录调试信息 - Log.Debug("订单验证通过", new { OrderId = order.Id }); - - // 使用 Warning 记录异常情况 - if (order.Amount > 10000) - { - Log.Warn("大额订单", new { OrderId = order.Id, Amount = order.Amount }); - } - - // 使用 Error 记录错误 - try - { - // 业务逻辑 - } - catch (Exception ex) - { - Log.Error("订单处理失败", ex, new { OrderId = order.Id }); - } - } -} -``` - -### 3. 记录上下文信息 - -```csharp -[Log("UserManagement")] -public partial class UserManager -{ - public void UpdateUserProfile(int userId, UserProfile profile) - { - Log.Info("更新用户资料", new - { - UserId = userId, - Changes = profile.GetChanges(), - Timestamp = DateTime.Now - }); - } -} -``` - -### 4. 在异常处理中使用日志 - -```csharp -[Log("DataAccess")] -public partial class UserRepository -{ - public async Task GetUserAsync(int userId) - { - try - { - return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); - } - catch (DbException ex) - { - Log.Error("数据库查询失败", ex, new { UserId = userId }); - throw new DataAccessException("无法获取用户信息", ex); - } - } -} -``` - -## 框架组件集成 - -### 在 GFramework 架构中使用 - -```csharp -[Log("Architecture")] -public partial class ExampleArchitecture : AbstractArchitecture -{ - protected override void Init() - { - Log.Info("ExampleArchitecture 初始化开始"); - - // 注册系统 - RegisterSystem(new GameSystem()); - RegisterSystem(new UISystem()); - - // 注册模型 - RegisterModel(new UserModel()); - RegisterModel(new GameModel()); - - // 注册工具 - RegisterUtility(new FileUtility()); - - Log.Info("ExampleArchitecture 初始化完成"); - } -} -``` - -### 在事件处理中使用 - -```csharp -[Log("Event")] -public partial class UserEventHandler -{ - [Log("EventHandler")] - public void OnUserLoginEvent(UserLoginEvent evt) - { - Log.Info("用户登录事件", new - { - UserId = evt.UserId, - LoginTime = evt.LoginTime, - IpAddress = evt.IpAddress - }); - } - - [Log("EventHandler")] - public void OnUserLogoutEvent(UserLogoutEvent evt) - { - Log.Info("用户登出事件", new - { - UserId = evt.UserId, - LogoutTime = evt.LogoutTime, - SessionDuration = evt.SessionDuration - }); - } -} -``` - -## 注意事项 - -1. **部分类**: 使用 `[Log]` 特性的类必须是 `partial` 类,因为生成器会添加字段到同一个类中。 - -2. **命名空间引用**: 确保项目中引用了 `GFramework.Generator.Attributes` 包。 - -3. **编译时生成**: 日志字段是在编译时生成的,不会影响运行时性能。 - -4. **多线程安全**: 生成的 `ILog` 实例是线程安全的,可以在多线程环境中使用。 - -5. **继承**: 派生类可以有自己的 `[Log]` 特性,也可以访问基类的受保护日志字段。 - -## 性能考虑 - -- **编译时生成**: 字段在编译时生成,没有运行时开销 -- **延迟初始化**: 日志记录器按需创建,避免不必要的对象创建 -- **级别检查**: 生成的代码包含级别检查,性能与手动创建的日志记录器相同 - -## 与现有日志系统的兼容性 - -生成的代码完全兼容现有的 `GFramework.Core.logging` 系统: - -```csharp -// 现有的方式仍然可以使用 -var manualLogger = Log.CreateLogger("Manual"); - -// 新生成的字段也可以正常使用 -[Log] -public partial class MyService -{ - public void Method() - { - // 两种方式都可以使用 - manualLogger.Info("手动创建的日志"); - Log.Info("自动生成的日志"); - } -} -``` - -通过使用 `[Log]` 特性,您可以显著减少样板代码,提高开发效率,同时保持日志记录的一致性和灵活性。 \ No newline at end of file diff --git a/GFramework.Godot.SourceGenerators.Attributes/GFramework.Godot.SourceGenerators.Attributes.csproj b/GFramework.Godot.SourceGenerators.Attributes/GFramework.Godot.SourceGenerators.Attributes.csproj new file mode 100644 index 0000000..237d661 --- /dev/null +++ b/GFramework.Godot.SourceGenerators.Attributes/GFramework.Godot.SourceGenerators.Attributes.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/GFramework.Godot.SourceGenerators.Attributes/logging/GodotLogAttribute.cs b/GFramework.Godot.SourceGenerators.Attributes/logging/GodotLogAttribute.cs new file mode 100644 index 0000000..4508d35 --- /dev/null +++ b/GFramework.Godot.SourceGenerators.Attributes/logging/GodotLogAttribute.cs @@ -0,0 +1,37 @@ +#nullable enable +namespace GFramework.SourceGenerators.Attributes.logging; + +/// +/// Godot日志特性,用于在类上标记以自动生成日志字段 +/// +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +public sealed class GodotLogAttribute : Attribute +{ + /// + /// 初始化 GodotLogAttribute 类的新实例 + /// + public GodotLogAttribute() + { + } + + /// + /// 初始化 GodotLogAttribute 类的新实例 + /// + /// 日志分类名 + public GodotLogAttribute(string? name) + { + Name = name; + } + + /// 日志分类名(默认使用类名) + public string? Name { get; set; } + + /// 生成字段名 + public string FieldName { get; set; } = "_log"; + + /// 是否生成 static 字段 + public bool IsStatic { get; set; } = true; + + /// 访问修饰符 + public string AccessModifier { get; set; } = "private"; +} \ No newline at end of file diff --git a/GFramework.Generator/AnalyzerReleases.Shipped.md b/GFramework.Godot.SourceGenerators/AnalyzerReleases.Shipped.md similarity index 100% rename from GFramework.Generator/AnalyzerReleases.Shipped.md rename to GFramework.Godot.SourceGenerators/AnalyzerReleases.Shipped.md diff --git a/GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md b/GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000..cb8e1c2 --- /dev/null +++ b/GFramework.Godot.SourceGenerators/AnalyzerReleases.Unshipped.md @@ -0,0 +1,9 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + + Rule ID | Category | Severity | Notes +------------|--------------------------|----------|------------------------ + GFLOG001 | GFramework.Godot.Logging | Error | GodotLoggerDiagnostics + GFW_LOG001 | GFramework.Godot.Logging | Warning | GodotLoggerDiagnostics \ No newline at end of file diff --git a/GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj b/GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj new file mode 100644 index 0000000..350752c --- /dev/null +++ b/GFramework.Godot.SourceGenerators/GFramework.Godot.SourceGenerators.csproj @@ -0,0 +1,19 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + + diff --git a/GFramework.Godot.SourceGenerators/logging/Diagnostic.cs b/GFramework.Godot.SourceGenerators/logging/Diagnostic.cs new file mode 100644 index 0000000..bf235c0 --- /dev/null +++ b/GFramework.Godot.SourceGenerators/logging/Diagnostic.cs @@ -0,0 +1,44 @@ +using Microsoft.CodeAnalysis; + +namespace GFramework.Godot.SourceGenerators.logging; + +/// +/// 提供诊断描述符的静态类,用于GFramework日志生成器的编译时检查 +/// +internal static class GodotLoggerDiagnostics +{ + /// + /// 诊断描述符:标识使用[GodotLog]特性的类必须声明为partial + /// + /// + /// ID: GFLOG001 + /// 严重性: Error + /// 分类: GFramework.Godot.Logging + /// + public static readonly DiagnosticDescriptor MustBePartial = + new( + id: "GFLOG001", + title: "Class must be partial", + messageFormat: "Class '{0}' must be declared as partial to use [GodotLog]", + category: "GFramework.Godot.Logging", + DiagnosticSeverity.Error, + isEnabledByDefault: true + ); + + + /// + /// 诊断描述符:标识GodotLogAttribute无法在指定类上生成Logger + /// + /// + /// ID: GFW_LOG001 + /// 严重性: Warning + /// 分类: GFramework.Godot.Logging + /// + public static readonly DiagnosticDescriptor LogAttributeInvalid = new( + id: "GFW_LOG001", + title: "GodotLogAttribute cannot generate Logger", + messageFormat: "GodotLogAttribute on class '{0}' is ineffective: {1}", + category: "GFramework.Godot.Logging", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); +} \ No newline at end of file diff --git a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs new file mode 100644 index 0000000..ef84ac4 --- /dev/null +++ b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs @@ -0,0 +1,181 @@ +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace GFramework.Godot.SourceGenerators.logging; + +/// +/// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段 +/// +[Generator] +public sealed class GodotLoggerGenerator : IIncrementalGenerator +{ + private const string AttributeMetadataName = "GFramework.SourceGenerators.Attributes.logging.GodotLogAttribute"; + private const string AttributeShortName = "GodotLogAttribute"; + private const string AttributeShortNameWithoutSuffix = "Log"; + + /// + /// 初始化生成器,设置语法过滤和代码生成逻辑 + /// + /// 增量生成器初始化上下文 + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // 1. 语法过滤:快速筛选候选类 + var targets = context.SyntaxProvider.CreateSyntaxProvider( + static (node, _) => + { + if (node is not ClassDeclarationSyntax cls) return false; + // 只要包含 Log 字眼的 Attribute 就先放行 + return cls.AttributeLists.SelectMany(a => a.Attributes).Any(a => + { + var name = a.Name.ToString(); + // 简单的字符串匹配,防止错过别名情况 + return name.Contains(AttributeShortNameWithoutSuffix); + }); + }, + static (ctx, _) => + { + var classDecl = (ClassDeclarationSyntax)ctx.Node; + var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl); + return (ClassDecl: classDecl, Symbol: symbol); + }) + .Where(x => x.Symbol is not null); + + // 2. 生成代码 + context.RegisterSourceOutput(targets, (spc, pair) => + { + try + { + var classDecl = pair.ClassDecl; + var classSymbol = pair.Symbol!; + + // 再次确认是否真的含有目标 Attribute (语义检查) + var attr = GetAttribute(classSymbol); + if (attr == null) return; // 可能是名字相似但不是我们要的 Attribute + + // 检查 partial + if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword)) + { + spc.ReportDiagnostic(Diagnostic.Create( + GodotLoggerDiagnostics.MustBePartial, + classDecl.Identifier.GetLocation(), + classSymbol.Name)); + + return; + } + + var source = Generate(classSymbol, attr); + var hintName = $"{classSymbol.Name}.Logger.g.cs"; + spc.AddSource(hintName, SourceText.From(source, Encoding.UTF8)); + } + catch (Exception ex) + { + // === 关键修复:生成错误报告文件 === + var errorSource = $"// source generator error: {ex.Message}\n// StackTrace:\n// {ex.StackTrace}"; + // 替换非法字符以防文件名报错 + var safeName = pair.Symbol?.Name ?? "Unknown"; + spc.AddSource($"{safeName}.Logger.Error.g.cs", SourceText.From(errorSource, Encoding.UTF8)); + } + }); + } + + /// + /// 获取类符号上的LogAttribute特性 + /// + /// 类符号 + /// LogAttribute特性数据,如果不存在则返回null + private static AttributeData? GetAttribute(INamedTypeSymbol classSymbol) + { + return classSymbol.GetAttributes().FirstOrDefault(a => + { + var cls = a.AttributeClass; + if (cls == null) return false; + + // 宽松匹配:全名匹配 OR 名字匹配 + return cls.ToDisplayString() == AttributeMetadataName || + cls.Name == AttributeShortName; + }); + } + + /// + /// 生成日志字段代码 + /// + /// 类符号 + /// LogAttribute特性数据 + /// 生成的C#代码字符串 + private static string Generate(INamedTypeSymbol classSymbol, AttributeData attr) + { + var ns = classSymbol.ContainingNamespace.IsGlobalNamespace + ? null + : classSymbol.ContainingNamespace.ToDisplayString(); + + var className = classSymbol.Name; + + // === 解析 Name === + var name = className; // 默认使用类名 + + // 检查是否有构造函数参数 + if (attr.ConstructorArguments.Length > 0) + { + var argValue = attr.ConstructorArguments[0].Value; + + name = argValue switch + { + // 情况 1: 参数存在,但值为 null (例如 [GodotLog] 且构造函数有默认值 null) + null => className, + // 情况 2: 参数存在,且是有效的字符串 (例如 [GodotLog("MyCategory")]) + string s when !string.IsNullOrWhiteSpace(s) => s, + _ => $"{className}_InvalidArg" + }; + } + + // === 解析 Named Arguments (更加安全的获取方式) === + var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log"; + var access = + GetNamedArg(attr, "AccessModifier")?.ToString() ?? + "private"; // 注意:如果你的 AccessModifier 是枚举,这里得到的可能是 int 或枚举名 + + // 处理 bool 类型 + var isStaticObj = GetNamedArg(attr, "IsStatic"); + var isStatic = isStaticObj is not bool b || b; // 默认为 true + + var staticKeyword = isStatic ? "static " : ""; + + var sb = new StringBuilder(); + sb.AppendLine("// "); + sb.AppendLine("using GFramework.Core.logging;"); // 确保这里引用了 ILogger + sb.AppendLine("using GFramework.Godot.logging;"); // 确保这里引用了 GodotLoggerFactory + + if (ns is not null) + { + sb.AppendLine($"namespace {ns}"); + sb.AppendLine("{"); + } + + sb.AppendLine($" public partial class {className}"); + sb.AppendLine(" {"); + sb.AppendLine(" /// Auto-generated logger"); + sb.AppendLine( + $" {access} {staticKeyword}readonly ILogger {fieldName} = " + + $"new GodotLoggerFactory.GetLogger(\"{name}\");"); + sb.AppendLine(" }"); + + if (ns is not null) + sb.AppendLine("}"); + + return sb.ToString(); + } + + /// + /// 从特性数据中获取命名参数的值 + /// + /// 特性数据 + /// 参数名称 + /// 参数值,如果不存在则返回null + private static object? GetNamedArg(AttributeData attr, string name) + { + return (from kv in attr.NamedArguments where kv.Key == name select kv.Value.Value).FirstOrDefault(); + } +} \ No newline at end of file diff --git a/GFramework.Generator.Attributes/GFramework.Generator.Attributes.csproj b/GFramework.SourceGenerators.Attributes/GFramework.SourceGenerators.Attributes.csproj similarity index 100% rename from GFramework.Generator.Attributes/GFramework.Generator.Attributes.csproj rename to GFramework.SourceGenerators.Attributes/GFramework.SourceGenerators.Attributes.csproj diff --git a/GFramework.Generator.Attributes/generator/enums/EnumExtensionsAttribute.cs b/GFramework.SourceGenerators.Attributes/enums/EnumExtensionsAttribute.cs similarity index 92% rename from GFramework.Generator.Attributes/generator/enums/EnumExtensionsAttribute.cs rename to GFramework.SourceGenerators.Attributes/enums/EnumExtensionsAttribute.cs index c3c90a7..8799cfa 100644 --- a/GFramework.Generator.Attributes/generator/enums/EnumExtensionsAttribute.cs +++ b/GFramework.SourceGenerators.Attributes/enums/EnumExtensionsAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace GFramework.Generator.Attributes.generator.enums +namespace GFramework.SourceGenerators.Attributes.enums { /// /// 标注在 enum 上,Source Generator 会为该 enum 生成扩展方法。 diff --git a/GFramework.Generator.Attributes/generator/logging/LogAttribute.cs b/GFramework.SourceGenerators.Attributes/logging/LogAttribute.cs similarity index 85% rename from GFramework.Generator.Attributes/generator/logging/LogAttribute.cs rename to GFramework.SourceGenerators.Attributes/logging/LogAttribute.cs index d933912..0f2894e 100644 --- a/GFramework.Generator.Attributes/generator/logging/LogAttribute.cs +++ b/GFramework.SourceGenerators.Attributes/logging/LogAttribute.cs @@ -1,7 +1,7 @@ #nullable enable using System; -namespace GFramework.Generator.Attributes.generator.logging; +namespace GFramework.SourceGenerators.Attributes.logging; /// /// 标注在类上,Source Generator 会为该类自动生成一个日志记录器字段。 @@ -9,6 +9,19 @@ namespace GFramework.Generator.Attributes.generator.logging; [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class LogAttribute : Attribute { + public LogAttribute() + { + } + + /// + /// 初始化 GodotLogAttribute 类的新实例 + /// + /// 日志分类名,默认使用类名 + public LogAttribute(string? name) + { + Name = name; + } + /// 日志分类名(默认使用类名) public string? Name { get; set; } @@ -20,13 +33,4 @@ public sealed class LogAttribute : Attribute /// 访问修饰符 public string AccessModifier { get; set; } = "private"; - public LogAttribute() { } - /// - /// 初始化 LogAttribute 类的新实例 - /// - /// 日志分类名,默认使用类名 - public LogAttribute(string? name) - { - Name = name; - } } \ No newline at end of file diff --git a/GFramework.SourceGenerators/AnalyzerReleases.Shipped.md b/GFramework.SourceGenerators/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000..60b59dd --- /dev/null +++ b/GFramework.SourceGenerators/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/GFramework.Generator/AnalyzerReleases.Unshipped.md b/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md similarity index 100% rename from GFramework.Generator/AnalyzerReleases.Unshipped.md rename to GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md diff --git a/GFramework.Generator/GFramework.Generator.csproj b/GFramework.SourceGenerators/GFramework.SourceGenerators.csproj similarity index 83% rename from GFramework.Generator/GFramework.Generator.csproj rename to GFramework.SourceGenerators/GFramework.SourceGenerators.csproj index f3e46ac..b62eee6 100644 --- a/GFramework.Generator/GFramework.Generator.csproj +++ b/GFramework.SourceGenerators/GFramework.SourceGenerators.csproj @@ -1,7 +1,7 @@  - GeWuYou.GFramework.Generator + GeWuYou.GFramework.SourceGenerators netstandard2.0 true true @@ -26,7 +26,7 @@ - @@ -35,12 +35,8 @@ - - - true - - diff --git a/GFramework.Generator/generator/enums/EnumExtensionsGenerator.cs b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs similarity index 96% rename from GFramework.Generator/generator/enums/EnumExtensionsGenerator.cs rename to GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs index f0bd865..9e31593 100644 --- a/GFramework.Generator/generator/enums/EnumExtensionsGenerator.cs +++ b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs @@ -5,12 +5,13 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -namespace GFramework.Generator.generator.enums; +namespace GFramework.SourceGenerators.enums; [Generator] public class EnumExtensionsGenerator : IIncrementalGenerator { - private const string AttributeFullName = "GFramework.Generator.Attributes.generator.enums.GenerateEnumExtensionsAttribute"; + private const string AttributeFullName = + "GFramework.SourceGenerators.Attributes.generator.enums.GenerateEnumExtensionsAttribute"; public void Initialize(IncrementalGeneratorInitializationContext context) { diff --git a/GFramework.Generator/generator/logging/Diagnostic.cs b/GFramework.SourceGenerators/logging/Diagnostic.cs similarity index 79% rename from GFramework.Generator/generator/logging/Diagnostic.cs rename to GFramework.SourceGenerators/logging/Diagnostic.cs index b52210e..10aea63 100644 --- a/GFramework.Generator/generator/logging/Diagnostic.cs +++ b/GFramework.SourceGenerators/logging/Diagnostic.cs @@ -1,13 +1,12 @@ using Microsoft.CodeAnalysis; -namespace GFramework.Generator.generator.logging; +namespace GFramework.SourceGenerators.logging; /// /// 提供诊断描述符的静态类,用于GFramework日志生成器的编译时检查 /// -internal static class Diagnostics +internal static class LoggerDiagnostics { - /// /// 定义诊断描述符:要求使用[Log]特性的类必须声明为partial /// @@ -23,17 +22,16 @@ internal static class Diagnostics DiagnosticSeverity.Error, isEnabledByDefault: true ); - + /// /// 定义诊断描述符:LogAttribute无法生成Logger的错误情况 /// public static readonly DiagnosticDescriptor LogAttributeInvalid = new( id: "GFW_LOG001", - title: "LogAttribute 无法生成 Logger", - messageFormat: "类 '{0}' 上的 LogAttribute 无法生效:{1}", - category: "GFramework.Logging", + title: "LogAttribute cannot generate Logger", + messageFormat: "LogAttribute on class '{0}' is ineffective: {1}", + category: "GFramework.Godot.Logging", DiagnosticSeverity.Warning, isEnabledByDefault: true); - -} +} \ No newline at end of file diff --git a/GFramework.SourceGenerators/logging/LoggerGenerator.cs b/GFramework.SourceGenerators/logging/LoggerGenerator.cs new file mode 100644 index 0000000..d71f4c3 --- /dev/null +++ b/GFramework.SourceGenerators/logging/LoggerGenerator.cs @@ -0,0 +1,185 @@ +#nullable enable + + +using System; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace GFramework.SourceGenerators.logging; + +/// +/// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段 +/// +[Generator] +public sealed class LoggerGenerator : IIncrementalGenerator +{ + private const string AttributeMetadataName = "GFramework.SourceGenerators.Attributes.logging.GodotLogAttribute"; + private const string AttributeShortName = "GodotLogAttribute"; + private const string AttributeShortNameWithoutSuffix = "Log"; + + /// + /// 初始化生成器,设置语法过滤和代码生成逻辑 + /// + /// 增量生成器初始化上下文 + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // 1. 语法过滤:快速筛选候选类 + var targets = context.SyntaxProvider.CreateSyntaxProvider( + static (node, _) => + { + if (node is not ClassDeclarationSyntax cls) return false; + // 只要包含 Log 字眼的 Attribute 就先放行 + return cls.AttributeLists.SelectMany(a => a.Attributes).Any(a => + { + var name = a.Name.ToString(); + // 简单的字符串匹配,防止错过别名情况 + return name.Contains(AttributeShortNameWithoutSuffix); + }); + }, + static (ctx, _) => + { + var classDecl = (ClassDeclarationSyntax)ctx.Node; + var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl); + return (ClassDecl: classDecl, Symbol: symbol); + }) + .Where(x => x.Symbol is not null); + + // 2. 生成代码 + context.RegisterSourceOutput(targets, (spc, pair) => + { + try + { + var classDecl = pair.ClassDecl; + var classSymbol = pair.Symbol!; + + // 再次确认是否真的含有目标 Attribute (语义检查) + var attr = GetAttribute(classSymbol); + if (attr == null) return; // 可能是名字相似但不是我们要的 Attribute + + // 检查 partial + if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword)) + { + spc.ReportDiagnostic(Diagnostic.Create( + LoggerDiagnostics.MustBePartial, + classDecl.Identifier.GetLocation(), + classSymbol.Name)); + + return; + } + + var source = Generate(classSymbol, attr); + var hintName = $"{classSymbol.Name}.Logger.g.cs"; + spc.AddSource(hintName, SourceText.From(source, Encoding.UTF8)); + } + catch (Exception ex) + { + // === 关键修复:生成错误报告文件 === + var errorSource = $"// source generator error: {ex.Message}\n// StackTrace:\n// {ex.StackTrace}"; + // 替换非法字符以防文件名报错 + var safeName = pair.Symbol?.Name ?? "Unknown"; + spc.AddSource($"{safeName}.Logger.Error.g.cs", SourceText.From(errorSource, Encoding.UTF8)); + } + }); + } + + /// + /// 获取类符号上的LogAttribute特性 + /// + /// 类符号 + /// LogAttribute特性数据,如果不存在则返回null + private static AttributeData? GetAttribute(INamedTypeSymbol classSymbol) + { + return classSymbol.GetAttributes().FirstOrDefault(a => + { + var cls = a.AttributeClass; + if (cls == null) return false; + + // 宽松匹配:全名匹配 OR 名字匹配 + return cls.ToDisplayString() == AttributeMetadataName || + cls.Name == AttributeShortName; + }); + } + + /// + /// 生成日志字段代码 + /// + /// 类符号 + /// LogAttribute特性数据 + /// 生成的C#代码字符串 + private static string Generate(INamedTypeSymbol classSymbol, AttributeData attr) + { + var ns = classSymbol.ContainingNamespace.IsGlobalNamespace + ? null + : classSymbol.ContainingNamespace.ToDisplayString(); + + var className = classSymbol.Name; + + // === 解析 Name === + var name = className; // 默认使用类名 + + // 检查是否有构造函数参数 + if (attr.ConstructorArguments.Length > 0) + { + var argValue = attr.ConstructorArguments[0].Value; + + name = argValue switch + { + // 情况 1: 参数存在,但值为 null (例如 [Log] 且构造函数有默认值 null) + null => className, + // 情况 2: 参数存在,且是有效的字符串 (例如 [Log("MyCategory")]) + string s when !string.IsNullOrWhiteSpace(s) => s, + _ => $"{className}_InvalidArg" + }; + } + + // === 解析 Named Arguments (更加安全的获取方式) === + var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log"; + var access = + GetNamedArg(attr, "AccessModifier")?.ToString() ?? + "private"; // 注意:如果你的 AccessModifier 是枚举,这里得到的可能是 int 或枚举名 + + // 处理 bool 类型 + var isStaticObj = GetNamedArg(attr, "IsStatic"); + var isStatic = isStaticObj is not bool b || b; // 默认为 true + + var staticKeyword = isStatic ? "static " : ""; + + var sb = new StringBuilder(); + sb.AppendLine("// "); + sb.AppendLine("using GFramework.Core.logging;"); // 确保这里引用了 ILog 和 Log 类 + + if (ns is not null) + { + sb.AppendLine($"namespace {ns}"); + sb.AppendLine("{"); + } + + sb.AppendLine($" public partial class {className}"); + sb.AppendLine(" {"); + sb.AppendLine($" /// Auto-generated logger"); + sb.AppendLine( + $" {access} {staticKeyword}readonly ILogger {fieldName} = " + + $"new ConsoleLoggerFactory.GetLogger(\"{name}\");"); + sb.AppendLine(" }"); + + if (ns is not null) + sb.AppendLine("}"); + + return sb.ToString(); + } + + /// + /// 从特性数据中获取命名参数的值 + /// + /// 特性数据 + /// 参数名称 + /// 参数值,如果不存在则返回null + private static object? GetNamedArg(AttributeData attr, string name) + { + return (from kv in attr.NamedArguments where kv.Key == name select kv.Value.Value).FirstOrDefault(); + } +} \ No newline at end of file diff --git a/GFramework.csproj b/GFramework.csproj index 17c72b4..a9aa03e 100644 --- a/GFramework.csproj +++ b/GFramework.csproj @@ -23,9 +23,27 @@ - - + + GFramework.SorceGenerators\bin\Debug\netstandard2.0\GFramework.Generator.Attributes.dll + + + GFramework.SorceGenerators\logging\README.md + + + GFramework.SorceGenerators\README.md + + + GFramework.SorceGenerators\AnalyzerReleases.Shipped.md + + + GFramework.SorceGenerators\AnalyzerReleases.Unshipped.md + + + + + + @@ -35,15 +53,30 @@ - - + + GFramework.SorceGenerators\enums\EnumExtensionsGenerator.cs + + + GFramework.SorceGenerators\logging\Diagnostic.cs + + + GFramework.SorceGenerators\logging\LoggerGenerator.cs + + + + + + - - + + + + + diff --git a/GFramework.sln b/GFramework.sln index 5af8702..a1f30d9 100644 --- a/GFramework.sln +++ b/GFramework.sln @@ -2,9 +2,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework ", "GFramework.csproj", "{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Generator", "GFramework.Generator\GFramework.Generator.csproj", "{E9D51809-0351-4B83-B85B-B5F469AAB3B8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.SourceGenerators", "GFramework.SourceGenerators\GFramework.SourceGenerators.csproj", "{E9D51809-0351-4B83-B85B-B5F469AAB3B8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Generator.Attributes", "GFramework.Generator.Attributes\GFramework.Generator.Attributes.csproj", "{84C5C3C9-5620-4924-BA04-92F813F2B70F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.SourceGenerators.Attributes", "GFramework.SourceGenerators.Attributes\GFramework.SourceGenerators.Attributes.csproj", "{84C5C3C9-5620-4924-BA04-92F813F2B70F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Core", "GFramework.Core\GFramework.Core.csproj", "{A6D5854D-79EA-487A-9ED9-396E6A1F8031}" EndProject @@ -12,6 +12,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot", "GFramew EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Game", "GFramework.Game\GFramework.Game.csproj", "{0B00816B-E8B2-4562-8C11-0C06CE761638}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGenerators", "GFramework.Godot.SourceGenerators\GFramework.Godot.SourceGenerators.csproj", "{C56FD287-CBC6-4C44-B3DF-103FA3660CA0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGenerators.Attributes", "GFramework.Godot.SourceGenerators.Attributes\GFramework.Godot.SourceGenerators.Attributes.csproj", "{3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,5 +46,13 @@ Global {0B00816B-E8B2-4562-8C11-0C06CE761638}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B00816B-E8B2-4562-8C11-0C06CE761638}.Release|Any CPU.ActiveCfg = Release|Any CPU {0B00816B-E8B2-4562-8C11-0C06CE761638}.Release|Any CPU.Build.0 = Release|Any CPU + {C56FD287-CBC6-4C44-B3DF-103FA3660CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C56FD287-CBC6-4C44-B3DF-103FA3660CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C56FD287-CBC6-4C44-B3DF-103FA3660CA0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C56FD287-CBC6-4C44-B3DF-103FA3660CA0}.Release|Any CPU.Build.0 = Release|Any CPU + {3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A1132B7-EC3B-4BB6-A752-8ADC92BC08A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal