GFramework/Directory.Build.targets
GeWuYou 2d1d1a43b6 chore(build): 移除静态全局using配置并实现自动化生成
- 删除所有手动维护的 buildTransitive props 文件
- 从项目文件中移除静态的 global usings 配置
- 删除废弃的 global-usings.modules.json 清单文件
- 移除旧的 TransitiveGlobalUsingsGenerationTests 测试
- 添加新的 TransitiveGlobalUsingsPackagingTests 验证自动化生成
- 在 Directory.Build.targets 中集成 MSBuild 自动化生成任务
- 实现基于源码扫描的动态命名空间发现机制
2026-03-24 22:24:52 +08:00

172 lines
9.3 KiB
XML

<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>