mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 21:34:28 +08:00
chore(build): 移除静态全局using配置并实现自动化生成
- 删除所有手动维护的 buildTransitive props 文件 - 从项目文件中移除静态的 global usings 配置 - 删除废弃的 global-usings.modules.json 清单文件 - 移除旧的 TransitiveGlobalUsingsGenerationTests 测试 - 添加新的 TransitiveGlobalUsingsPackagingTests 验证自动化生成 - 在 Directory.Build.targets 中集成 MSBuild 自动化生成任务 - 实现基于源码扫描的动态命名空间发现机制
This commit is contained in:
parent
b80f46b6fa
commit
2d1d1a43b6
171
Directory.Build.targets
Normal file
171
Directory.Build.targets
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<Project>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
为 GFramework 运行时包生成可选的模块级 transitive global usings。
|
||||||
|
该逻辑只在明确启用的可打包项目中生效,并在构建/打包期间自动扫描源码命名空间。
|
||||||
|
-->
|
||||||
|
<UsingTask TaskName="GenerateGFrameworkTransitiveGlobalUsingsProps"
|
||||||
|
TaskFactory="RoslynCodeTaskFactory"
|
||||||
|
AssemblyFile="$(MSBuildToolsPath)/Microsoft.Build.Tasks.Core.dll">
|
||||||
|
<ParameterGroup>
|
||||||
|
<SourceFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true"/>
|
||||||
|
<ExcludedNamespaces ParameterType="Microsoft.Build.Framework.ITaskItem[]"/>
|
||||||
|
<ExcludedNamespacePrefixes ParameterType="Microsoft.Build.Framework.ITaskItem[]"/>
|
||||||
|
<OutputFile ParameterType="System.String" Required="true"/>
|
||||||
|
<NamespaceItemName ParameterType="System.String" Required="true"/>
|
||||||
|
</ParameterGroup>
|
||||||
|
<Task>
|
||||||
|
<Code Type="Fragment"
|
||||||
|
Language="cs"><![CDATA[
|
||||||
|
var discoveredNamespaces = new global::System.Collections.Generic.SortedSet<string>(global::System.StringComparer.Ordinal);
|
||||||
|
var exactExclusions = new global::System.Collections.Generic.HashSet<string>(global::System.StringComparer.Ordinal);
|
||||||
|
var prefixExclusions = new global::System.Collections.Generic.List<string>();
|
||||||
|
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("<Project>");
|
||||||
|
builder.AppendLine(" <!-- This file is generated by GFramework's MSBuild transitive global usings pipeline. -->");
|
||||||
|
builder.AppendLine(" <!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->");
|
||||||
|
builder.AppendLine(" <!-- Add <GFrameworkExcludedUsing Include=\"Namespace\" /> to opt out of specific namespaces. -->");
|
||||||
|
builder.Append(" <ItemGroup Condition=\"'");
|
||||||
|
builder.Append(msbuildPropertyOpen);
|
||||||
|
builder.AppendLine("EnableGFrameworkGlobalUsings)' == 'true'\">");
|
||||||
|
|
||||||
|
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(" <Using Include=\"");
|
||||||
|
builder.Append(msbuildItemOpen);
|
||||||
|
builder.Append(NamespaceItemName);
|
||||||
|
builder.AppendLine(")\" />");
|
||||||
|
builder.AppendLine(" </ItemGroup>");
|
||||||
|
builder.AppendLine("</Project>");
|
||||||
|
|
||||||
|
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}.");
|
||||||
|
]]></Code>
|
||||||
|
</Task>
|
||||||
|
</UsingTask>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<_GFrameworkTransitiveGlobalUsingsEnabled Condition="'$(EnableGFrameworkPackageTransitiveGlobalUsings)' == 'true' and '$(IsPackable)' != 'false'">true</_GFrameworkTransitiveGlobalUsingsEnabled>
|
||||||
|
<_GFrameworkTransitiveGlobalUsingsPrimaryTargetFramework Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and '$(TargetFrameworks)' != ''">$([System.String]::Copy('$(TargetFrameworks)').Split(';')[0])</_GFrameworkTransitiveGlobalUsingsPrimaryTargetFramework>
|
||||||
|
<_GFrameworkTransitiveGlobalUsingsGenerationBuild Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and ('$(TargetFrameworks)' == '' or '$(TargetFramework)' == '$(_GFrameworkTransitiveGlobalUsingsPrimaryTargetFramework)')">true</_GFrameworkTransitiveGlobalUsingsGenerationBuild>
|
||||||
|
<_GFrameworkTransitiveGlobalUsingsPackageId Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and '$(PackageId)' != ''">$(PackageId)</_GFrameworkTransitiveGlobalUsingsPackageId>
|
||||||
|
<_GFrameworkTransitiveGlobalUsingsPackageId Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true' and '$(_GFrameworkTransitiveGlobalUsingsPackageId)' == ''">$(AssemblyName)</_GFrameworkTransitiveGlobalUsingsPackageId>
|
||||||
|
<_GFrameworkTransitiveGlobalUsingsOutputFile Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true'">$(BaseIntermediateOutputPath)gframework/$(_GFrameworkTransitiveGlobalUsingsPackageId).props</_GFrameworkTransitiveGlobalUsingsOutputFile>
|
||||||
|
<_GFrameworkTransitiveGlobalUsingsItemName Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true'">_$([System.Text.RegularExpressions.Regex]::Replace('$(MSBuildProjectName)', '[^A-Za-z0-9_]', '_'))_TransitiveUsing</_GFrameworkTransitiveGlobalUsingsItemName>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(_GFrameworkTransitiveGlobalUsingsEnabled)' == 'true'">
|
||||||
|
<None Include="$(_GFrameworkTransitiveGlobalUsingsOutputFile)"
|
||||||
|
Pack="true"
|
||||||
|
PackagePath="buildTransitive"
|
||||||
|
Visible="false"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="GenerateGFrameworkModuleTransitiveGlobalUsings"
|
||||||
|
Condition="'$(_GFrameworkTransitiveGlobalUsingsGenerationBuild)' == 'true'"
|
||||||
|
BeforeTargets="CoreCompile;GenerateNuspec">
|
||||||
|
<GenerateGFrameworkTransitiveGlobalUsingsProps
|
||||||
|
SourceFiles="@(Compile->'%(FullPath)')"
|
||||||
|
ExcludedNamespaces="@(GFrameworkTransitiveUsingExclude)"
|
||||||
|
ExcludedNamespacePrefixes="@(GFrameworkTransitiveUsingExcludePrefix)"
|
||||||
|
OutputFile="$(_GFrameworkTransitiveGlobalUsingsOutputFile)"
|
||||||
|
NamespaceItemName="$(_GFrameworkTransitiveGlobalUsingsItemName)"/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@ -10,6 +10,7 @@
|
|||||||
<MeziantouPolyfill_IncludedPolyfills>T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute</MeziantouPolyfill_IncludedPolyfills>
|
<MeziantouPolyfill_IncludedPolyfills>T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute</MeziantouPolyfill_IncludedPolyfills>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- 引入必要的命名空间 -->
|
<!-- 引入必要的命名空间 -->
|
||||||
@ -27,9 +28,4 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Mediator.Abstractions" Version="3.0.1"/>
|
<PackageReference Include="Mediator.Abstractions" Version="3.0.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- <auto-generated gframework-transitive-global-usings> -->
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="buildTransitive\GeWuYou.GFramework.Core.Abstractions.props" Pack="true" PackagePath="buildTransitive" Visible="false"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<!-- </auto-generated gframework-transitive-global-usings> -->
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<!-- This file is generated by scripts/generate-module-global-usings.py. -->
|
|
||||||
<!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->
|
|
||||||
<!-- Add <GFrameworkExcludedUsing Include="Namespace" /> to opt out of specific namespaces. -->
|
|
||||||
<ItemGroup Condition="'$(EnableGFrameworkGlobalUsings)' == 'true'">
|
|
||||||
<_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)"/>
|
|
||||||
<Using Include="@(_GFramework_Core_Abstractions_TransitiveUsing)"/>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace GFramework.Core.Tests.Packaging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 验证模块级可选 Global Usings 的生成脚本与仓库中的已提交产物保持同步。
|
|
||||||
/// 该测试用于防止新增模块、命名空间清单或打包声明发生漂移后静默进入仓库。
|
|
||||||
/// </summary>
|
|
||||||
[TestFixture]
|
|
||||||
public class TransitiveGlobalUsingsGenerationTests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 验证生成脚本的检查模式可以在当前仓库状态下通过。
|
|
||||||
/// 如果此断言失败,说明清单、生成的 props 文件或 csproj 打包声明至少有一处未同步。
|
|
||||||
/// </summary>
|
|
||||||
[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}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启动生成脚本的检查模式。
|
|
||||||
/// 该模式不会修改仓库内容,只验证仓库中的生成产物是否已与当前规则对齐。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repositoryRoot">仓库根目录。</param>
|
|
||||||
/// <returns>已启动的进程实例。</returns>
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从测试输出目录向上回溯,定位包含解决方案文件的仓库根目录。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>仓库根目录绝对路径。</returns>
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.Packaging;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证运行时模块在构建期间会自动生成 transitive global usings 资产。
|
||||||
|
/// 该测试覆盖命名空间自动发现、框架侧过滤和消费者侧排除钩子的最终构建产物。
|
||||||
|
/// </summary>
|
||||||
|
[TestFixture]
|
||||||
|
public class TransitiveGlobalUsingsPackagingTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 验证 GFramework.Core 在构建后会生成 transitive global usings props,
|
||||||
|
/// 且 props 内容来自源码自动发现,并保留消费者侧排除机制。
|
||||||
|
/// </summary>
|
||||||
|
[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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从测试输出目录向上回溯,定位包含解决方案文件的仓库根目录。
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>仓库根目录绝对路径。</returns>
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@
|
|||||||
<ImplicitUsings>disable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj"/>
|
<ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj"/>
|
||||||
@ -14,9 +15,4 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5"/>
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5"/>
|
||||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0"/>
|
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- <auto-generated gframework-transitive-global-usings> -->
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="buildTransitive\GeWuYou.GFramework.Core.props" Pack="true" PackagePath="buildTransitive" Visible="false"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<!-- </auto-generated gframework-transitive-global-usings> -->
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<!-- This file is generated by scripts/generate-module-global-usings.py. -->
|
|
||||||
<!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->
|
|
||||||
<!-- Add <GFrameworkExcludedUsing Include="Namespace" /> to opt out of specific namespaces. -->
|
|
||||||
<ItemGroup Condition="'$(EnableGFrameworkGlobalUsings)' == 'true'">
|
|
||||||
<_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)"/>
|
|
||||||
<Using Include="@(_GFramework_Core_TransitiveUsing)"/>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -4,14 +4,10 @@
|
|||||||
<PackageId>GeWuYou.$(AssemblyName)</PackageId>
|
<PackageId>GeWuYou.$(AssemblyName)</PackageId>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj" PrivateAssets="all"/>
|
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj" PrivateAssets="all"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- <auto-generated gframework-transitive-global-usings> -->
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="buildTransitive\GeWuYou.GFramework.Ecs.Arch.Abstractions.props" Pack="true" PackagePath="buildTransitive" Visible="false"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<!-- </auto-generated gframework-transitive-global-usings> -->
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<!-- This file is generated by scripts/generate-module-global-usings.py. -->
|
|
||||||
<!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->
|
|
||||||
<!-- Add <GFrameworkExcludedUsing Include="Namespace" /> to opt out of specific namespaces. -->
|
|
||||||
<ItemGroup Condition="'$(EnableGFrameworkGlobalUsings)' == 'true'">
|
|
||||||
<_GFramework_Ecs_Arch_Abstractions_TransitiveUsing Include="GFramework.Ecs.Arch.Abstractions"/>
|
|
||||||
<_GFramework_Ecs_Arch_Abstractions_TransitiveUsing Remove="@(GFrameworkExcludedUsing)"/>
|
|
||||||
<Using Include="@(_GFramework_Ecs_Arch_Abstractions_TransitiveUsing)"/>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -7,6 +7,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
|
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -19,9 +20,4 @@
|
|||||||
<PackageReference Include="Arch" Version="2.1.0"/>
|
<PackageReference Include="Arch" Version="2.1.0"/>
|
||||||
<PackageReference Include="Arch.System" Version="1.1.0"/>
|
<PackageReference Include="Arch.System" Version="1.1.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- <auto-generated gframework-transitive-global-usings> -->
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="buildTransitive\GeWuYou.GFramework.Ecs.Arch.props" Pack="true" PackagePath="buildTransitive" Visible="false"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<!-- </auto-generated gframework-transitive-global-usings> -->
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<!-- This file is generated by scripts/generate-module-global-usings.py. -->
|
|
||||||
<!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->
|
|
||||||
<!-- Add <GFrameworkExcludedUsing Include="Namespace" /> to opt out of specific namespaces. -->
|
|
||||||
<ItemGroup Condition="'$(EnableGFrameworkGlobalUsings)' == 'true'">
|
|
||||||
<_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)"/>
|
|
||||||
<Using Include="@(_GFramework_Ecs_Arch_TransitiveUsing)"/>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -10,6 +10,7 @@
|
|||||||
<MeziantouPolyfill_IncludedPolyfills>T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute</MeziantouPolyfill_IncludedPolyfills>
|
<MeziantouPolyfill_IncludedPolyfills>T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute</MeziantouPolyfill_IncludedPolyfills>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/>
|
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/>
|
||||||
@ -28,9 +29,4 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- <auto-generated gframework-transitive-global-usings> -->
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="buildTransitive\GeWuYou.GFramework.Game.Abstractions.props" Pack="true" PackagePath="buildTransitive" Visible="false"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<!-- </auto-generated gframework-transitive-global-usings> -->
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<!-- This file is generated by scripts/generate-module-global-usings.py. -->
|
|
||||||
<!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->
|
|
||||||
<!-- Add <GFrameworkExcludedUsing Include="Namespace" /> to opt out of specific namespaces. -->
|
|
||||||
<ItemGroup Condition="'$(EnableGFrameworkGlobalUsings)' == 'true'">
|
|
||||||
<_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)"/>
|
|
||||||
<Using Include="@(_GFramework_Game_Abstractions_TransitiveUsing)"/>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -6,6 +6,7 @@
|
|||||||
<ImplicitUsings>disable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
|
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
|
||||||
@ -14,9 +15,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4"/>
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- <auto-generated gframework-transitive-global-usings> -->
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="buildTransitive\GeWuYou.GFramework.Game.props" Pack="true" PackagePath="buildTransitive" Visible="false"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<!-- </auto-generated gframework-transitive-global-usings> -->
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<!-- This file is generated by scripts/generate-module-global-usings.py. -->
|
|
||||||
<!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->
|
|
||||||
<!-- Add <GFrameworkExcludedUsing Include="Namespace" /> to opt out of specific namespaces. -->
|
|
||||||
<ItemGroup Condition="'$(EnableGFrameworkGlobalUsings)' == 'true'">
|
|
||||||
<_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)"/>
|
|
||||||
<Using Include="@(_GFramework_Game_TransitiveUsing)"/>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -6,6 +6,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<EnableGFrameworkPackageTransitiveGlobalUsings>true</EnableGFrameworkPackageTransitiveGlobalUsings>
|
||||||
<!-- Godot.SourceGenerators expects this property from Godot.NET.Sdk.
|
<!-- Godot.SourceGenerators expects this property from Godot.NET.Sdk.
|
||||||
Provide a safe default so source generators can run in plain SDK-style builds as well. -->
|
Provide a safe default so source generators can run in plain SDK-style builds as well. -->
|
||||||
<GodotProjectDir Condition="'$(GodotProjectDir)' == ''">$(MSBuildProjectDirectory)</GodotProjectDir>
|
<GodotProjectDir Condition="'$(GodotProjectDir)' == ''">$(MSBuildProjectDirectory)</GodotProjectDir>
|
||||||
@ -22,9 +23,4 @@
|
|||||||
<ProjectReference Include="..\GFramework.Game.Abstractions\GFramework.Game.Abstractions.csproj" PrivateAssets="all"/>
|
<ProjectReference Include="..\GFramework.Game.Abstractions\GFramework.Game.Abstractions.csproj" PrivateAssets="all"/>
|
||||||
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj" PrivateAssets="all"/>
|
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj" PrivateAssets="all"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- <auto-generated gframework-transitive-global-usings> -->
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="buildTransitive\GeWuYou.GFramework.Godot.props" Pack="true" PackagePath="buildTransitive" Visible="false"/>
|
|
||||||
</ItemGroup>
|
|
||||||
<!-- </auto-generated gframework-transitive-global-usings> -->
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<!-- This file is generated by scripts/generate-module-global-usings.py. -->
|
|
||||||
<!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->
|
|
||||||
<!-- Add <GFrameworkExcludedUsing Include="Namespace" /> to opt out of specific namespaces. -->
|
|
||||||
<ItemGroup Condition="'$(EnableGFrameworkGlobalUsings)' == 'true'">
|
|
||||||
<_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)"/>
|
|
||||||
<Using Include="@(_GFramework_Godot_TransitiveUsing)"/>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -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 gframework-transitive-global-usings> -->"
|
|
||||||
AUTO_GENERATED_END = "<!-- </auto-generated gframework-transitive-global-usings> -->"
|
|
||||||
|
|
||||||
|
|
||||||
@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"<IsPackable>\s*false\s*</IsPackable>", 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"<PackageId>(.*?)</PackageId>", 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 = [
|
|
||||||
"<Project>",
|
|
||||||
" <!-- This file is generated by scripts/generate-module-global-usings.py. -->",
|
|
||||||
" <!-- EnableGFrameworkGlobalUsings=true enables the transitive global usings from this package. -->",
|
|
||||||
" <!-- Add <GFrameworkExcludedUsing Include=\"Namespace\" /> to opt out of specific namespaces. -->",
|
|
||||||
" <ItemGroup Condition=\"'$(EnableGFrameworkGlobalUsings)' == 'true'\">",
|
|
||||||
]
|
|
||||||
|
|
||||||
for namespace in module.namespaces:
|
|
||||||
lines.append(f" <{item_name} Include=\"{namespace}\" />")
|
|
||||||
|
|
||||||
lines.extend(
|
|
||||||
[
|
|
||||||
f" <{item_name} Remove=\"@(GFrameworkExcludedUsing)\" />",
|
|
||||||
f" <Using Include=\"@({item_name})\" />",
|
|
||||||
" </ItemGroup>",
|
|
||||||
"</Project>",
|
|
||||||
"",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
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}",
|
|
||||||
" <ItemGroup>",
|
|
||||||
f" <None Include=\"{props_path}\" Pack=\"true\" PackagePath=\"buildTransitive\" Visible=\"false\"/>",
|
|
||||||
" </ItemGroup>",
|
|
||||||
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("</Project>", f"{block}</Project>")
|
|
||||||
|
|
||||||
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()
|
|
||||||
Loading…
x
Reference in New Issue
Block a user