From 2d1d1a43b640da30cbb7835df9a895e5606a1bfd Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:24:52 +0800 Subject: [PATCH] =?UTF-8?q?chore(build):=20=E7=A7=BB=E9=99=A4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E5=85=A8=E5=B1=80using=E9=85=8D=E7=BD=AE=E5=B9=B6?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8=E5=8C=96=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除所有手动维护的 buildTransitive props 文件 - 从项目文件中移除静态的 global usings 配置 - 删除废弃的 global-usings.modules.json 清单文件 - 移除旧的 TransitiveGlobalUsingsGenerationTests 测试 - 添加新的 TransitiveGlobalUsingsPackagingTests 验证自动化生成 - 在 Directory.Build.targets 中集成 MSBuild 自动化生成任务 - 实现基于源码扫描的动态命名空间发现机制 --- Directory.Build.targets | 171 ++++++++++++++ .../GFramework.Core.Abstractions.csproj | 6 +- ...GeWuYou.GFramework.Core.Abstractions.props | 47 ---- .../TransitiveGlobalUsingsGenerationTests.cs | 74 ------ .../TransitiveGlobalUsingsPackagingTests.cs | 56 +++++ GFramework.Core/GFramework.Core.csproj | 6 +- .../GeWuYou.GFramework.Core.props | 53 ----- .../GFramework.Ecs.Arch.Abstractions.csproj | 6 +- ...You.GFramework.Ecs.Arch.Abstractions.props | 10 - .../GFramework.Ecs.Arch.csproj | 6 +- .../GeWuYou.GFramework.Ecs.Arch.props | 13 - .../GFramework.Game.Abstractions.csproj | 6 +- ...GeWuYou.GFramework.Game.Abstractions.props | 19 -- GFramework.Game/GFramework.Game.csproj | 6 +- .../GeWuYou.GFramework.Game.props | 21 -- GFramework.Godot/GFramework.Godot.csproj | 6 +- .../GeWuYou.GFramework.Godot.props | 22 -- global-usings.modules.json | 161 ------------- scripts/generate-module-global-usings.py | 222 ------------------ 19 files changed, 234 insertions(+), 677 deletions(-) create mode 100644 Directory.Build.targets delete mode 100644 GFramework.Core.Abstractions/buildTransitive/GeWuYou.GFramework.Core.Abstractions.props delete mode 100644 GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsGenerationTests.cs create mode 100644 GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsPackagingTests.cs delete mode 100644 GFramework.Core/buildTransitive/GeWuYou.GFramework.Core.props delete mode 100644 GFramework.Ecs.Arch.Abstractions/buildTransitive/GeWuYou.GFramework.Ecs.Arch.Abstractions.props delete mode 100644 GFramework.Ecs.Arch/buildTransitive/GeWuYou.GFramework.Ecs.Arch.props delete mode 100644 GFramework.Game.Abstractions/buildTransitive/GeWuYou.GFramework.Game.Abstractions.props delete mode 100644 GFramework.Game/buildTransitive/GeWuYou.GFramework.Game.props delete mode 100644 GFramework.Godot/buildTransitive/GeWuYou.GFramework.Godot.props delete mode 100644 global-usings.modules.json delete mode 100644 scripts/generate-module-global-usings.py diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..1b1ad0a --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,171 @@ + + + + + + + + + + + + + (global::System.StringComparer.Ordinal); + var exactExclusions = new global::System.Collections.Generic.HashSet(global::System.StringComparer.Ordinal); + var prefixExclusions = new global::System.Collections.Generic.List(); + var namespacePattern = new global::System.Text.RegularExpressions.Regex( + @"^\s*namespace\s+([A-Za-z_][A-Za-z0-9_.]*)\s*(?:;|\{)", + global::System.Text.RegularExpressions.RegexOptions.Compiled); + + if (ExcludedNamespaces != null) + { + foreach (var excludedNamespace in ExcludedNamespaces) + { + if (!string.IsNullOrWhiteSpace(excludedNamespace.ItemSpec)) + { + exactExclusions.Add(excludedNamespace.ItemSpec.Trim()); + } + } + } + + if (ExcludedNamespacePrefixes != null) + { + foreach (var excludedPrefix in ExcludedNamespacePrefixes) + { + if (!string.IsNullOrWhiteSpace(excludedPrefix.ItemSpec)) + { + prefixExclusions.Add(excludedPrefix.ItemSpec.Trim()); + } + } + } + + foreach (var sourceFile in SourceFiles) + { + var path = sourceFile.ItemSpec; + if (!global::System.IO.File.Exists(path)) + { + continue; + } + + foreach (var line in global::System.IO.File.ReadLines(path)) + { + var match = namespacePattern.Match(line); + if (!match.Success) + { + continue; + } + + var namespaceName = match.Groups[1].Value; + if (!namespaceName.StartsWith("GFramework.", global::System.StringComparison.Ordinal)) + { + continue; + } + + if (exactExclusions.Contains(namespaceName)) + { + continue; + } + + var excludedByPrefix = false; + foreach (var prefix in prefixExclusions) + { + if (namespaceName.StartsWith(prefix, global::System.StringComparison.Ordinal)) + { + excludedByPrefix = true; + break; + } + } + + if (!excludedByPrefix) + { + discoveredNamespaces.Add(namespaceName); + } + } + } + + static string Escape(string value) + { + return global::System.Security.SecurityElement.Escape(value) ?? value; + } + + var directory = global::System.IO.Path.GetDirectoryName(OutputFile); + if (!string.IsNullOrEmpty(directory)) + { + global::System.IO.Directory.CreateDirectory(directory); + } + + var builder = new global::System.Text.StringBuilder(); + var msbuildPropertyOpen = new string(new[] { '$', '(' }); + var msbuildItemOpen = new string(new[] { '@', '(' }); + builder.AppendLine(""); + builder.AppendLine(" "); + builder.AppendLine(" "); + builder.AppendLine(" "); + builder.Append(" "); + + foreach (var namespaceName in discoveredNamespaces) + { + builder.Append(" <"); + builder.Append(NamespaceItemName); + builder.Append(" Include=\""); + builder.Append(Escape(namespaceName)); + builder.AppendLine("\" />"); + } + + builder.Append(" <"); + builder.Append(NamespaceItemName); + builder.Append(" Remove=\""); + builder.Append(msbuildItemOpen); + builder.AppendLine("GFrameworkExcludedUsing)\" />"); + builder.Append(" "); + builder.AppendLine(" "); + builder.AppendLine(""); + + global::System.IO.File.WriteAllText(OutputFile, builder.ToString(), new global::System.Text.UTF8Encoding(false)); + Log.LogMessage(global::Microsoft.Build.Framework.MessageImportance.Low, + $"Generated {discoveredNamespaces.Count} transitive global usings for {OutputFile}."); + ]]> + + + + + <_GFrameworkTransitiveGlobalUsingsEnabled Condition="'$(EnableGFrameworkPackageTransitiveGlobalUsings)' == 'true' and '$(IsPackable)' != 'false'">true + <_GFrameworkTransitiveGlobalUsingsPrimaryTargetFramework Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and '$(TargetFrameworks)' != ''">$([System.String]::Copy('$(TargetFrameworks)').Split(';')[0]) + <_GFrameworkTransitiveGlobalUsingsGenerationBuild Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and ('$(TargetFrameworks)' == '' or '$(TargetFramework)' == '$(_GFrameworkTransitiveGlobalUsingsPrimaryTargetFramework)')">true + <_GFrameworkTransitiveGlobalUsingsPackageId Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and '$(PackageId)' != ''">$(PackageId) + <_GFrameworkTransitiveGlobalUsingsPackageId Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and '$(_GFrameworkTransitiveGlobalUsingsPackageId)' == ''">$(AssemblyName) + <_GFrameworkTransitiveGlobalUsingsOutputFile Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true'">$(BaseIntermediateOutputPath)gframework/$(_GFrameworkTransitiveGlobalUsingsPackageId).props + <_GFrameworkTransitiveGlobalUsingsItemName Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true'">_$([System.Text.RegularExpressions.Regex]::Replace('$(MSBuildProjectName)', '[^A-Za-z0-9_]', '_'))_TransitiveUsing + + + + + + + + + + + diff --git a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj index d5f1779..83dea9d 100644 --- a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj +++ b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj @@ -10,6 +10,7 @@ T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute enable true + true @@ -27,9 +28,4 @@ - - - - - diff --git a/GFramework.Core.Abstractions/buildTransitive/GeWuYou.GFramework.Core.Abstractions.props b/GFramework.Core.Abstractions/buildTransitive/GeWuYou.GFramework.Core.Abstractions.props deleted file mode 100644 index 67cca44..0000000 --- a/GFramework.Core.Abstractions/buildTransitive/GeWuYou.GFramework.Core.Abstractions.props +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Architectures"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Bases"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Command"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Concurrency"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Configuration"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Controller"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Coroutine"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Cqrs"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Cqrs.Command"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Cqrs.Notification"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Cqrs.Query"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Cqrs.Request"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Data"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Enums"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Environment"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Events"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Ioc"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Lifecycle"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Localization"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Logging"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Model"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Pause"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Pool"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Properties"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Property"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Query"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Registries"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Resource"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Rule"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Serializer"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.State"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.StateManagement"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Storage"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Systems"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Time"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Utility"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Utility.Numeric"/> - <_GFramework_Core_Abstractions_TransitiveUsing Include="GFramework.Core.Abstractions.Versioning"/> - <_GFramework_Core_Abstractions_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/> - - - diff --git a/GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsGenerationTests.cs b/GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsGenerationTests.cs deleted file mode 100644 index e840a86..0000000 --- a/GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsGenerationTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Diagnostics; -using System.IO; - -namespace GFramework.Core.Tests.Packaging; - -/// -/// 验证模块级可选 Global Usings 的生成脚本与仓库中的已提交产物保持同步。 -/// 该测试用于防止新增模块、命名空间清单或打包声明发生漂移后静默进入仓库。 -/// -[TestFixture] -public class TransitiveGlobalUsingsGenerationTests -{ - /// - /// 验证生成脚本的检查模式可以在当前仓库状态下通过。 - /// 如果此断言失败,说明清单、生成的 props 文件或 csproj 打包声明至少有一处未同步。 - /// - [Test] - public void GenerateModuleGlobalUsingsScript_CheckMode_Should_Pass() - { - var repositoryRoot = FindRepositoryRoot(); - using var process = StartScriptCheck(repositoryRoot); - - var standardOutput = process.StandardOutput.ReadToEnd(); - var standardError = process.StandardError.ReadToEnd(); - process.WaitForExit(); - - Assert.That( - process.ExitCode, - Is.EqualTo(0), - $"Expected module global usings generation to be up to date.{System.Environment.NewLine}" + - $"stdout:{System.Environment.NewLine}{standardOutput}{System.Environment.NewLine}" + - $"stderr:{System.Environment.NewLine}{standardError}"); - } - - /// - /// 启动生成脚本的检查模式。 - /// 该模式不会修改仓库内容,只验证仓库中的生成产物是否已与当前规则对齐。 - /// - /// 仓库根目录。 - /// 已启动的进程实例。 - private static Process StartScriptCheck(string repositoryRoot) - { - var startInfo = new ProcessStartInfo("python3", "scripts/generate-module-global-usings.py --check") - { - WorkingDirectory = repositoryRoot, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false - }; - - return Process.Start(startInfo) - ?? throw new InvalidOperationException("Failed to start the module global usings generation check."); - } - - /// - /// 从测试输出目录向上回溯,定位包含解决方案文件的仓库根目录。 - /// - /// 仓库根目录绝对路径。 - private static string FindRepositoryRoot() - { - var currentDirectory = new DirectoryInfo(TestContext.CurrentContext.TestDirectory); - - while (currentDirectory != null) - { - var solutionPath = Path.Combine(currentDirectory.FullName, "GFramework.sln"); - if (File.Exists(solutionPath)) - return currentDirectory.FullName; - - currentDirectory = currentDirectory.Parent; - } - - throw new DirectoryNotFoundException("Could not locate the repository root for GFramework."); - } -} \ No newline at end of file diff --git a/GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsPackagingTests.cs b/GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsPackagingTests.cs new file mode 100644 index 0000000..2ca6a9c --- /dev/null +++ b/GFramework.Core.Tests/Packaging/TransitiveGlobalUsingsPackagingTests.cs @@ -0,0 +1,56 @@ +using System.IO; + +namespace GFramework.Core.Tests.Packaging; + +/// +/// 验证运行时模块在构建期间会自动生成 transitive global usings 资产。 +/// 该测试覆盖命名空间自动发现、框架侧过滤和消费者侧排除钩子的最终构建产物。 +/// +[TestFixture] +public class TransitiveGlobalUsingsPackagingTests +{ + /// + /// 验证 GFramework.Core 在构建后会生成 transitive global usings props, + /// 且 props 内容来自源码自动发现,并保留消费者侧排除机制。 + /// + [Test] + public void CoreBuild_Should_Generate_AutoDiscovered_TransitiveGlobalUsingsProps() + { + var repositoryRoot = FindRepositoryRoot(); + var propsPath = Path.Combine( + repositoryRoot, + "GFramework.Core", + "obj", + "gframework", + "GeWuYou.GFramework.Core.props"); + + Assert.That(File.Exists(propsPath), Is.True, $"Expected generated props to exist: {propsPath}"); + var propsContent = File.ReadAllText(propsPath); + + Assert.That(propsContent, Does.Contain("GFramework.Core.Extensions")); + Assert.That(propsContent, Does.Contain("GFramework.Core.Architectures")); + Assert.That(propsContent, Does.Contain("GFramework.Core.Coroutine.Extensions")); + Assert.That(propsContent, Does.Contain("Remove=\"@(GFrameworkExcludedUsing)\"")); + Assert.That(propsContent, Does.Not.Contain("System.Runtime.CompilerServices")); + } + + /// + /// 从测试输出目录向上回溯,定位包含解决方案文件的仓库根目录。 + /// + /// 仓库根目录绝对路径。 + private static string FindRepositoryRoot() + { + var currentDirectory = new DirectoryInfo(TestContext.CurrentContext.TestDirectory); + + while (currentDirectory != null) + { + var solutionPath = Path.Combine(currentDirectory.FullName, "GFramework.sln"); + if (File.Exists(solutionPath)) + return currentDirectory.FullName; + + currentDirectory = currentDirectory.Parent; + } + + throw new DirectoryNotFoundException("Could not locate the repository root for GFramework."); + } +} \ No newline at end of file diff --git a/GFramework.Core/GFramework.Core.csproj b/GFramework.Core/GFramework.Core.csproj index 0ef4bcd..c450b44 100644 --- a/GFramework.Core/GFramework.Core.csproj +++ b/GFramework.Core/GFramework.Core.csproj @@ -6,6 +6,7 @@ disable enable true + true @@ -14,9 +15,4 @@ - - - - - diff --git a/GFramework.Core/buildTransitive/GeWuYou.GFramework.Core.props b/GFramework.Core/buildTransitive/GeWuYou.GFramework.Core.props deleted file mode 100644 index b666aa9..0000000 --- a/GFramework.Core/buildTransitive/GeWuYou.GFramework.Core.props +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Architectures"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Command"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Concurrency"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Configuration"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Constants"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Coroutine"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Coroutine.Extensions"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Coroutine.Instructions"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Cqrs.Behaviors"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Cqrs.Command"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Cqrs.Notification"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Cqrs.Query"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Cqrs.Request"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Environment"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Events"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Events.Filters"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Extensions"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Functional"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Functional.Async"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Functional.Control"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Functional.Functions"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Functional.Pipe"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Ioc"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Localization"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Localization.Formatters"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Logging"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Logging.Appenders"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Logging.Filters"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Logging.Formatters"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Model"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Pause"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Pool"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Property"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Query"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Resource"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Rule"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Services"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Services.Modules"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.State"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.StateManagement"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Systems"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Time"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Utility"/> - <_GFramework_Core_TransitiveUsing Include="GFramework.Core.Utility.Numeric"/> - <_GFramework_Core_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/> - - - diff --git a/GFramework.Ecs.Arch.Abstractions/GFramework.Ecs.Arch.Abstractions.csproj b/GFramework.Ecs.Arch.Abstractions/GFramework.Ecs.Arch.Abstractions.csproj index d9550a3..94337e8 100644 --- a/GFramework.Ecs.Arch.Abstractions/GFramework.Ecs.Arch.Abstractions.csproj +++ b/GFramework.Ecs.Arch.Abstractions/GFramework.Ecs.Arch.Abstractions.csproj @@ -4,14 +4,10 @@ GeWuYou.$(AssemblyName) true enable + true - - - - - diff --git a/GFramework.Ecs.Arch.Abstractions/buildTransitive/GeWuYou.GFramework.Ecs.Arch.Abstractions.props b/GFramework.Ecs.Arch.Abstractions/buildTransitive/GeWuYou.GFramework.Ecs.Arch.Abstractions.props deleted file mode 100644 index 5816a35..0000000 --- a/GFramework.Ecs.Arch.Abstractions/buildTransitive/GeWuYou.GFramework.Ecs.Arch.Abstractions.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - <_GFramework_Ecs_Arch_Abstractions_TransitiveUsing Include="GFramework.Ecs.Arch.Abstractions"/> - <_GFramework_Ecs_Arch_Abstractions_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/> - - - diff --git a/GFramework.Ecs.Arch/GFramework.Ecs.Arch.csproj b/GFramework.Ecs.Arch/GFramework.Ecs.Arch.csproj index 68a8916..a79bec7 100644 --- a/GFramework.Ecs.Arch/GFramework.Ecs.Arch.csproj +++ b/GFramework.Ecs.Arch/GFramework.Ecs.Arch.csproj @@ -7,6 +7,7 @@ enable true true + true @@ -19,9 +20,4 @@ - - - - - diff --git a/GFramework.Ecs.Arch/buildTransitive/GeWuYou.GFramework.Ecs.Arch.props b/GFramework.Ecs.Arch/buildTransitive/GeWuYou.GFramework.Ecs.Arch.props deleted file mode 100644 index 1776e61..0000000 --- a/GFramework.Ecs.Arch/buildTransitive/GeWuYou.GFramework.Ecs.Arch.props +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - <_GFramework_Ecs_Arch_TransitiveUsing Include="GFramework.Ecs.Arch"/> - <_GFramework_Ecs_Arch_TransitiveUsing Include="GFramework.Ecs.Arch.Components"/> - <_GFramework_Ecs_Arch_TransitiveUsing Include="GFramework.Ecs.Arch.Extensions"/> - <_GFramework_Ecs_Arch_TransitiveUsing Include="GFramework.Ecs.Arch.Systems"/> - <_GFramework_Ecs_Arch_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/> - - - diff --git a/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj b/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj index 62aef63..b4a8890 100644 --- a/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj +++ b/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj @@ -10,6 +10,7 @@ T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute enable true + true @@ -28,9 +29,4 @@ runtime; build; native; contentfiles; analyzers - - - - - diff --git a/GFramework.Game.Abstractions/buildTransitive/GeWuYou.GFramework.Game.Abstractions.props b/GFramework.Game.Abstractions/buildTransitive/GeWuYou.GFramework.Game.Abstractions.props deleted file mode 100644 index f7319ff..0000000 --- a/GFramework.Game.Abstractions/buildTransitive/GeWuYou.GFramework.Game.Abstractions.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Asset"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Data"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Data.Events"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Enums"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Routing"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Scene"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Setting"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Setting.Data"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.Storage"/> - <_GFramework_Game_Abstractions_TransitiveUsing Include="GFramework.Game.Abstractions.UI"/> - <_GFramework_Game_Abstractions_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/> - - - diff --git a/GFramework.Game/GFramework.Game.csproj b/GFramework.Game/GFramework.Game.csproj index 37bc8a8..5bb94d9 100644 --- a/GFramework.Game/GFramework.Game.csproj +++ b/GFramework.Game/GFramework.Game.csproj @@ -6,6 +6,7 @@ disable enable true + true @@ -14,9 +15,4 @@ - - - - - diff --git a/GFramework.Game/buildTransitive/GeWuYou.GFramework.Game.props b/GFramework.Game/buildTransitive/GeWuYou.GFramework.Game.props deleted file mode 100644 index b7749c7..0000000 --- a/GFramework.Game/buildTransitive/GeWuYou.GFramework.Game.props +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Data"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Extensions"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Routing"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Scene"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Scene.Handler"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Serializer"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Setting"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Setting.Events"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.State"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.Storage"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.UI"/> - <_GFramework_Game_TransitiveUsing Include="GFramework.Game.UI.Handler"/> - <_GFramework_Game_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/> - - - diff --git a/GFramework.Godot/GFramework.Godot.csproj b/GFramework.Godot/GFramework.Godot.csproj index 44e037c..29c065c 100644 --- a/GFramework.Godot/GFramework.Godot.csproj +++ b/GFramework.Godot/GFramework.Godot.csproj @@ -6,6 +6,7 @@ enable net8.0;net9.0;net10.0 true + true $(MSBuildProjectDirectory) @@ -22,9 +23,4 @@ - - - - - diff --git a/GFramework.Godot/buildTransitive/GeWuYou.GFramework.Godot.props b/GFramework.Godot/buildTransitive/GeWuYou.GFramework.Godot.props deleted file mode 100644 index ac14337..0000000 --- a/GFramework.Godot/buildTransitive/GeWuYou.GFramework.Godot.props +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Architectures"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Coroutine"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Data"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Extensions"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Extensions.Signal"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Logging"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Pause"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Pool"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Scene"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Setting"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Setting.Data"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.Storage"/> - <_GFramework_Godot_TransitiveUsing Include="GFramework.Godot.UI"/> - <_GFramework_Godot_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/> - - - diff --git a/global-usings.modules.json b/global-usings.modules.json deleted file mode 100644 index 6db53da..0000000 --- a/global-usings.modules.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "modules": [ - { - "project": "GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj", - "namespaces": [ - "GFramework.Core.Abstractions.Architectures", - "GFramework.Core.Abstractions.Bases", - "GFramework.Core.Abstractions.Command", - "GFramework.Core.Abstractions.Concurrency", - "GFramework.Core.Abstractions.Configuration", - "GFramework.Core.Abstractions.Controller", - "GFramework.Core.Abstractions.Coroutine", - "GFramework.Core.Abstractions.Cqrs", - "GFramework.Core.Abstractions.Cqrs.Command", - "GFramework.Core.Abstractions.Cqrs.Notification", - "GFramework.Core.Abstractions.Cqrs.Query", - "GFramework.Core.Abstractions.Cqrs.Request", - "GFramework.Core.Abstractions.Data", - "GFramework.Core.Abstractions.Enums", - "GFramework.Core.Abstractions.Environment", - "GFramework.Core.Abstractions.Events", - "GFramework.Core.Abstractions.Ioc", - "GFramework.Core.Abstractions.Lifecycle", - "GFramework.Core.Abstractions.Localization", - "GFramework.Core.Abstractions.Logging", - "GFramework.Core.Abstractions.Model", - "GFramework.Core.Abstractions.Pause", - "GFramework.Core.Abstractions.Pool", - "GFramework.Core.Abstractions.Properties", - "GFramework.Core.Abstractions.Property", - "GFramework.Core.Abstractions.Query", - "GFramework.Core.Abstractions.Registries", - "GFramework.Core.Abstractions.Resource", - "GFramework.Core.Abstractions.Rule", - "GFramework.Core.Abstractions.Serializer", - "GFramework.Core.Abstractions.State", - "GFramework.Core.Abstractions.StateManagement", - "GFramework.Core.Abstractions.Storage", - "GFramework.Core.Abstractions.Systems", - "GFramework.Core.Abstractions.Time", - "GFramework.Core.Abstractions.Utility", - "GFramework.Core.Abstractions.Utility.Numeric", - "GFramework.Core.Abstractions.Versioning" - ] - }, - { - "project": "GFramework.Core/GFramework.Core.csproj", - "namespaces": [ - "GFramework.Core.Architectures", - "GFramework.Core.Command", - "GFramework.Core.Concurrency", - "GFramework.Core.Configuration", - "GFramework.Core.Constants", - "GFramework.Core.Coroutine", - "GFramework.Core.Coroutine.Extensions", - "GFramework.Core.Coroutine.Instructions", - "GFramework.Core.Cqrs.Behaviors", - "GFramework.Core.Cqrs.Command", - "GFramework.Core.Cqrs.Notification", - "GFramework.Core.Cqrs.Query", - "GFramework.Core.Cqrs.Request", - "GFramework.Core.Environment", - "GFramework.Core.Events", - "GFramework.Core.Events.Filters", - "GFramework.Core.Extensions", - "GFramework.Core.Functional", - "GFramework.Core.Functional.Async", - "GFramework.Core.Functional.Control", - "GFramework.Core.Functional.Functions", - "GFramework.Core.Functional.Pipe", - "GFramework.Core.Ioc", - "GFramework.Core.Localization", - "GFramework.Core.Localization.Formatters", - "GFramework.Core.Logging", - "GFramework.Core.Logging.Appenders", - "GFramework.Core.Logging.Filters", - "GFramework.Core.Logging.Formatters", - "GFramework.Core.Model", - "GFramework.Core.Pause", - "GFramework.Core.Pool", - "GFramework.Core.Property", - "GFramework.Core.Query", - "GFramework.Core.Resource", - "GFramework.Core.Rule", - "GFramework.Core.Services", - "GFramework.Core.Services.Modules", - "GFramework.Core.State", - "GFramework.Core.StateManagement", - "GFramework.Core.Systems", - "GFramework.Core.Time", - "GFramework.Core.Utility", - "GFramework.Core.Utility.Numeric" - ] - }, - { - "project": "GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj", - "namespaces": [ - "GFramework.Game.Abstractions.Asset", - "GFramework.Game.Abstractions.Data", - "GFramework.Game.Abstractions.Data.Events", - "GFramework.Game.Abstractions.Enums", - "GFramework.Game.Abstractions.Routing", - "GFramework.Game.Abstractions.Scene", - "GFramework.Game.Abstractions.Setting", - "GFramework.Game.Abstractions.Setting.Data", - "GFramework.Game.Abstractions.Storage", - "GFramework.Game.Abstractions.UI" - ] - }, - { - "project": "GFramework.Game/GFramework.Game.csproj", - "namespaces": [ - "GFramework.Game.Data", - "GFramework.Game.Extensions", - "GFramework.Game.Routing", - "GFramework.Game.Scene", - "GFramework.Game.Scene.Handler", - "GFramework.Game.Serializer", - "GFramework.Game.Setting", - "GFramework.Game.Setting.Events", - "GFramework.Game.State", - "GFramework.Game.Storage", - "GFramework.Game.UI", - "GFramework.Game.UI.Handler" - ] - }, - { - "project": "GFramework.Ecs.Arch.Abstractions/GFramework.Ecs.Arch.Abstractions.csproj", - "namespaces": [ - "GFramework.Ecs.Arch.Abstractions" - ] - }, - { - "project": "GFramework.Ecs.Arch/GFramework.Ecs.Arch.csproj", - "namespaces": [ - "GFramework.Ecs.Arch", - "GFramework.Ecs.Arch.Components", - "GFramework.Ecs.Arch.Extensions", - "GFramework.Ecs.Arch.Systems" - ] - }, - { - "project": "GFramework.Godot/GFramework.Godot.csproj", - "namespaces": [ - "GFramework.Godot.Architectures", - "GFramework.Godot.Coroutine", - "GFramework.Godot.Data", - "GFramework.Godot.Extensions", - "GFramework.Godot.Extensions.Signal", - "GFramework.Godot.Logging", - "GFramework.Godot.Pause", - "GFramework.Godot.Pool", - "GFramework.Godot.Scene", - "GFramework.Godot.Setting", - "GFramework.Godot.Setting.Data", - "GFramework.Godot.Storage", - "GFramework.Godot.UI" - ] - } - ] -} diff --git a/scripts/generate-module-global-usings.py b/scripts/generate-module-global-usings.py deleted file mode 100644 index ba040cc..0000000 --- a/scripts/generate-module-global-usings.py +++ /dev/null @@ -1,222 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import annotations - -import argparse -import json -import re -from dataclasses import dataclass -from pathlib import Path - - -ROOT_DIR = Path(__file__).resolve().parent.parent -CONFIG_PATH = ROOT_DIR / "global-usings.modules.json" -AUTO_GENERATED_START = "" -AUTO_GENERATED_END = "" - - -@dataclass(frozen=True) -class ModuleConfig: - project_path: Path - namespaces: tuple[str, ...] - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Generate optional transitive global usings for packable GFramework runtime modules.", - ) - parser.add_argument( - "--check", - action="store_true", - help="Validate that generated files are up to date instead of writing them.", - ) - return parser.parse_args() - - -def load_text(path: Path) -> tuple[str, str]: - raw = path.read_bytes() - if raw.startswith(b"\xef\xbb\xbf"): - return raw.decode("utf-8-sig"), "utf-8-sig" - - return raw.decode("utf-8"), "utf-8" - - -def write_text(path: Path, content: str, encoding: str) -> None: - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(content, encoding=encoding) - - -def load_modules() -> list[ModuleConfig]: - data = json.loads(CONFIG_PATH.read_text(encoding="utf-8")) - modules: list[ModuleConfig] = [] - - for entry in data["modules"]: - project_path = ROOT_DIR / entry["project"] - namespaces = tuple(dict.fromkeys(entry["namespaces"])) - modules.append(ModuleConfig(project_path=project_path, namespaces=namespaces)) - - return modules - - -def discover_runtime_modules() -> list[Path]: - projects: list[Path] = [] - - for project_path in sorted(ROOT_DIR.rglob("*.csproj")): - if "obj" in project_path.parts or "bin" in project_path.parts: - continue - - if project_path.name == "GFramework.csproj": - continue - - project_name = project_path.stem - if not project_name.startswith("GFramework."): - continue - - if ".Tests" in project_name or "SourceGenerators" in project_name: - continue - - csproj_text, _ = load_text(project_path) - if re.search(r"\s*false\s*", csproj_text, re.IGNORECASE): - continue - - projects.append(project_path) - - return projects - - -def resolve_package_id(project_path: Path) -> str: - project_text, _ = load_text(project_path) - match = re.search(r"(.*?)", project_text, re.DOTALL) - if match is None: - return project_path.stem - - package_id = match.group(1).strip() - return package_id.replace("$(AssemblyName)", project_path.stem) - - -def sanitize_identifier(text: str) -> str: - return re.sub(r"[^A-Za-z0-9_]", "_", text) - - -def render_props(module: ModuleConfig) -> str: - item_name = f"_{sanitize_identifier(module.project_path.stem)}_TransitiveUsing" - lines = [ - "", - " ", - " ", - " ", - " ", - ] - - for namespace in module.namespaces: - lines.append(f" <{item_name} Include=\"{namespace}\" />") - - lines.extend( - [ - f" <{item_name} Remove=\"@(GFrameworkExcludedUsing)\" />", - f" ", - " ", - "", - "", - ], - ) - return "\n".join(lines) - - -def render_pack_block(package_id: str) -> str: - props_path = f"buildTransitive\\{package_id}.props" - return "\n".join( - [ - f" {AUTO_GENERATED_START}", - " ", - f" ", - " ", - f" {AUTO_GENERATED_END}", - "", - ], - ) - - -def update_csproj(project_path: Path, package_id: str) -> tuple[str, str]: - project_text, encoding = load_text(project_path) - block = render_pack_block(package_id) - pattern = re.compile( - rf"\s*{re.escape(AUTO_GENERATED_START)}.*?{re.escape(AUTO_GENERATED_END)}\s*", - re.DOTALL, - ) - - if pattern.search(project_text): - updated = pattern.sub(lambda _: f"\n{block}", project_text) - else: - updated = project_text.replace("", f"{block}") - - return updated, encoding - - -def validate_module_coverage(modules: list[ModuleConfig]) -> None: - configured_projects = {module.project_path.resolve() for module in modules} - discovered_projects = {project.resolve() for project in discover_runtime_modules()} - - missing = sorted(project.relative_to(ROOT_DIR) for project in discovered_projects - configured_projects) - extra = sorted(project.relative_to(ROOT_DIR) for project in configured_projects - discovered_projects) - - if missing or extra: - messages: list[str] = [] - if missing: - messages.append("Unconfigured runtime modules:\n - " + "\n - ".join(str(path) for path in missing)) - if extra: - messages.append("Configured modules that are not eligible runtime packages:\n - " + - "\n - ".join(str(path) for path in extra)) - raise SystemExit("\n".join(messages)) - - -def check_or_write(path: Path, expected: str, encoding: str, check_only: bool, changed: list[Path]) -> None: - if path.exists(): - current, _ = load_text(path) - if current == expected: - return - elif check_only: - changed.append(path) - return - - if check_only: - changed.append(path) - return - - write_text(path, expected, encoding) - changed.append(path) - - -def main() -> None: - args = parse_args() - modules = load_modules() - validate_module_coverage(modules) - changed: list[Path] = [] - - for module in modules: - package_id = resolve_package_id(module.project_path) - props_path = module.project_path.parent / "buildTransitive" / f"{package_id}.props" - props_content = render_props(module) - check_or_write(props_path, props_content, "utf-8", args.check, changed) - - updated_project, encoding = update_csproj(module.project_path, package_id) - check_or_write(module.project_path, updated_project, encoding, args.check, changed) - - if args.check: - if changed: - relative_paths = "\n".join(f" - {path.relative_to(ROOT_DIR)}" for path in changed) - raise SystemExit(f"Generated module global usings are out of date:\n{relative_paths}") - - print("Module global usings are up to date.") - return - - if changed: - for path in changed: - print(f"Updated {path.relative_to(ROOT_DIR)}") - return - - print("No module global usings changes were needed.") - - -if __name__ == "__main__": - main()