fix(Build): centralize generated artifact exclusions

This commit is contained in:
chinboy
2026-08-16 11:08:01 +08:00
parent b586d905ec
commit ca55fc175c
14 changed files with 101 additions and 73 deletions
+2
View File
@@ -163,6 +163,8 @@ tools/*
!tools/AtomUI.Docs.LLMsGenerator/**
!tools/performances/
!tools/performances/**
tools/**/[Bb]in/
tools/**/[Oo]bj/
.nuget
artifacts/
nuget
+4
View File
@@ -13,6 +13,10 @@
<None Remove="*.csproj.DotSettings" />
</ItemGroup>
<ItemGroup Condition="'$(CompilerGeneratedFilesOutputPath)' != ''">
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
</ItemGroup>
<Import Project="$(MSBuildThisFileDirectory)AtomUI.Generator.targets" />
<Target Name="AtomUIPrepareGeneratorConsumerPackageAssets"
@@ -25,10 +25,6 @@
<InternalsVisibleTo Include="AtomUIGallery.Tests" />
</ItemGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../src/AtomUI.Toolkits.GalleryBase/AtomUI.Toolkits.GalleryBase.csproj"/>
<ProjectReference Include="../../src/AtomUI.Desktop.Controls/AtomUI.Desktop.Controls.csproj"/>
@@ -41,9 +41,9 @@ NativeAOT 应用和测试,补充 Homebrew OpenSSL/Brotli linker 搜索路径
`$(AtomUIBuildTasksAssembly)`、显式 NuGet build asset 清单和 Generator tool asset 清单,然后导入
`AtomUI.Generator.props`。
`AtomUI.Repository.targets` 定义第一方库的 AOT/Trim 默认值、排除 `.DotSettings`,导入
`AtomUI.Generator.targets`,并为声明 `AtomUIRegistrationPackageId` 的产品包注入 consumer target、共享构建资产和
工具程序集。
`AtomUI.Repository.targets` 定义第一方库的 AOT/Trim 默认值,集中排除 `.DotSettings` 和项目目录中的 compiler-generated
源码快照,导入 `AtomUI.Generator.targets`,并为声明 `AtomUIRegistrationPackageId` 的产品包注入 consumer target、
共享构建资产和工具程序集。
`AtomUI.Repository.props` 是 Generator build assets 的唯一清单:
@@ -53,6 +53,10 @@ NativeAOT 应用和测试,补充 Homebrew OpenSSL/Brotli linker 搜索路径
`tools/netstandard2.0/`。
- `AtomUI.Generator.csproj` 与注册型产品包只能消费这两个 item,不得各自维护第二份文件清单。
`OutputPaths.props` 对仓库内所有项目统一设置 `PackageOutputPath`、`OutputPath` 和 `BaseIntermediateOutputPath`。这同样
适用于 `tools/` 下的项目;工具源码的 `.gitignore` 反向规则之后必须重新排除 `tools/**/bin/` 和 `tools/**/obj/`,
避免配置迁移或 IDE 中间态产生的本地二进制进入待提交列表。
## NuGet 入口
`build/AtomUI.Generator.props` 和 `build/AtomUI.Generator.targets` 是包根自动导入入口。它们分别导入 Linked
@@ -108,17 +112,17 @@ Repository 配置、`MacOSHomebrewNativeAot.targets` 或 `scripts/` 资产。
## 源生成输出
多个项目启用:
需要把源生成结果写到仓库目录的项目只声明标准 SDK 属性:
```xml
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>GeneratedFiles</CompilerGeneratedFilesOutputPath>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
```
`GeneratedFiles/` 是本地编译产物,默认由 `.gitignore` 忽略;只有 GalleryBase 中被结构测试直接读取的少量快照保留跟踪。
生成文件输出到项目内 `GeneratedFiles/`,但从编译输入中移除该目录,避免重复编译。源生成器通过 Analyzer 方式参与当前编译。
`AtomUI.Repository.targets` 根据 `CompilerGeneratedFilesOutputPath` 统一从 `Compile` 移除这些快照,避免第二次构建把上次
生成结果作为普通源码再次编译。项目文件不得重复声明同一条 `Compile Remove`。源生成器通过 Analyzer 方式参与当前编译。
## Analyzer 引用方式
@@ -91,8 +91,8 @@ Directory.Build.targets
```
`AtomUI.Repository.props` 在导入 Generator props 前设置源码构建使用的 `$(AtomUIBuildTasksAssembly)`。Repository
targets 同时拥有第一方库 AOT/Trim 默认值、`.DotSettings` 排除和产品包 build asset 注入,不再为这些短逻辑保留
独立聚合文件。
targets 同时拥有第一方库 AOT/Trim 默认值、`.DotSettings` 与 compiler-generated 源码快照排除,以及产品包 build
asset 注入,不再为这些短逻辑保留独立聚合文件。
## 文件职责
@@ -103,7 +103,7 @@ targets 同时拥有第一方库 AOT/Trim 默认值、`.DotSettings` 排除和
| `PackageMetadata.props` | NuGet 公共元数据及仓库资产路径 |
| `OutputPaths.props` | NuGet、编译输出和集中式中间产物路径 |
| `AtomUI.Repository.props` | Repository props 聚合、Build Tasks 源码路径、NuGet/tool 显式资产清单 |
| `AtomUI.Repository.targets` | AOT/Trim 默认值、产品包资产注入、Generator targets 聚合 |
| `AtomUI.Repository.targets` | AOT/Trim 默认值、生成源码快照排除、产品包资产注入、Generator targets 聚合 |
| `AtomUI.Generator.props/targets` | NuGet 自动导入入口和 feature 编排 |
| `AtomUI.GeneratorConsumer.targets` | 注册型产品包 consumer target 模板 |
| `AtomUI.LinkedRegistration.*` | linked publish 注册构建管线 |
@@ -198,6 +198,8 @@ Repository 构建在 `AtomUI.Repository.props` 中把它指向集中输出目录
- Generator 和 Build Tasks 不进入应用运行时依赖、输出或 publish 目录。
- Localization target 名称、时序和行为保持等价。
- 集中输出布局不会把项目目录中的旧 `obj/**/*.cs` 重新纳入 Compile。
- 设置 `CompilerGeneratedFilesOutputPath` 的项目由 Repository targets 统一排除生成源码,不在各 `.csproj` 重复规则。
- `tools/` 源码反向 ignore 规则之后重新排除 `bin/` 和 `obj/`,中间态构建产物不能进入版本控制候选。
## 验证
@@ -7,10 +7,6 @@
<CompilerGeneratedFilesOutputPath>GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" />
</ItemGroup>
@@ -11,10 +11,6 @@
<AtomUILanguageContractVersion>2</AtomUILanguageContractVersion>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" />
<PackageReference Include="Svg.Controls.Avalonia" />
-4
View File
@@ -7,10 +7,6 @@
<CompilerGeneratedFilesOutputPath>GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../AtomUI.Localization/AtomUI.Localization.csproj" />
<ProjectReference Include="../AtomUI.Generator/AtomUI.Generator.csproj"
@@ -14,10 +14,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<AvaloniaXaml Update="Themes/ColorSlider/AbstractColorPickerSliderTrackTheme.axaml;Themes/ColorSlider/ColorPickerSliderTrackTheme.axaml;Themes/ColorSlider/ColorSliderTrackButtonTheme.axaml;Themes/ColorSlider/GradientColorPickerTrackTheme.axaml;Themes/ColorView/ColorPickerInput.axaml"
AtomUIRegistrationUnit="ColorPicker" />
@@ -12,10 +12,6 @@
<RootNamespace>AtomUI.Desktop.Controls</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<AvaloniaXaml Update="Themes/DataGridOperationButtons.axaml;Themes/DataGridRowExpanderTheme.axaml;Themes/Filters/DataGridFilterMenuItemTheme.axaml;Themes/Filters/DataGridFilterTreeItemTheme.axaml"
AtomUIRegistrationUnit="DataGrid" />
@@ -11,10 +11,6 @@
<RootNamespace>AtomUI.Desktop.Controls</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../AtomUI.Generator/AtomUI.Generator.csproj"
OutputItemType="Analyzer"
@@ -12,12 +12,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
<Compile Remove="Window\Reflection\**" />
<Compile Remove="Window\Visuals\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ReactiveUI.Avalonia"/>
<PackageReference Include="Avalonia.Desktop"/>
@@ -38,30 +32,4 @@
<ProjectReference Include="../AtomUI.Controls/AtomUI.Controls.csproj" />
<ProjectReference Include="../AtomUI.Generator/AtomUI.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<AvaloniaXaml Remove="Window\Reflection\**" />
<AvaloniaXaml Remove="Window\Visuals\**" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Window\Reflection\**" />
<EmbeddedResource Remove="Window\Visuals\**" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Remove="Window\Reflection\**" />
<AdditionalFiles Remove="Window\Visuals\**" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Remove="Window\Reflection\**" />
<AvaloniaResource Remove="Window\Visuals\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Window\Reflection\**" />
<None Remove="Window\Visuals\**" />
</ItemGroup>
</Project>
@@ -12,10 +12,6 @@
<AtomUILanguageContractVersion>2</AtomUILanguageContractVersion>
</PropertyGroup>
<ItemGroup>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/>
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="AtomUI.Toolkits.GalleryBase.Tests" />
</ItemGroup>
@@ -159,6 +159,86 @@ public sealed class BuildLayoutTests
excludes.Value.ShouldContain("$(MSBuildProjectDirectory)/obj/**");
}
[Fact]
public void Repository_Output_Paths_Centralize_Binaries_And_Intermediate_Files()
{
var outputPaths = XDocument.Load(GetRepoFile("build/OutputPaths.props"));
outputPaths.Descendants("PackageOutputPath")
.ShouldHaveSingleItem()
.Value.ShouldBe("$(MSBuildThisFileDirectory)../output/Nuget/$(Configuration)");
outputPaths.Descendants("OutputPathWithoutFramework")
.ShouldHaveSingleItem()
.Value.ShouldBe("$(MSBuildThisFileDirectory)../output/bin/$(Configuration)");
outputPaths.Descendants("OutputPath")
.ShouldHaveSingleItem()
.Value.ShouldBe("$(OutputPathWithoutFramework)");
outputPaths.Descendants("BaseIntermediateOutputPath")
.ShouldHaveSingleItem()
.Value.ShouldBe(
"$(MSBuildThisFileDirectory)../output/$(MSBuildProjectName)/obj");
}
[Fact]
public void Tool_Source_Exceptions_Do_Not_Expose_Build_Output_Directories()
{
var ignoreLines = File.ReadAllLines(GetRepoFile(".gitignore"));
var lastToolSourceException = Array.FindLastIndex(ignoreLines, line =>
line is "!tools/AtomUI.Docs.LLMsGenerator/**" or "!tools/performances/**");
var binIgnore = Array.IndexOf(ignoreLines, "tools/**/[Bb]in/");
var objIgnore = Array.IndexOf(ignoreLines, "tools/**/[Oo]bj/");
lastToolSourceException.ShouldBeGreaterThanOrEqualTo(0);
binIgnore.ShouldBeGreaterThan(lastToolSourceException);
objIgnore.ShouldBeGreaterThan(lastToolSourceException);
}
[Fact]
public void Repository_Targets_Exclude_Compiler_Generated_Files_Once()
{
const string generatedFilesPattern = "$(CompilerGeneratedFilesOutputPath)/**/*.cs";
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
var removal = repositoryTargets.Descendants("Compile")
.Where(element =>
(string?)element.Attribute("Remove") == generatedFilesPattern)
.ShouldHaveSingleItem();
((string?)removal.Parent?.Attribute("Condition"))
.ShouldBe("'$(CompilerGeneratedFilesOutputPath)' != ''");
var repositoryRoot = GetRepositoryRoot();
var projectFiles = new[] { "src", "controlgallery", "tests", "tools" }
.Select(directory => Path.Combine(repositoryRoot, directory))
.Where(Directory.Exists)
.SelectMany(directory => Directory.EnumerateFiles(
directory,
"*.csproj",
SearchOption.AllDirectories));
foreach (var projectFile in projectFiles)
{
var project = XDocument.Load(projectFile);
if (!project.Descendants("CompilerGeneratedFilesOutputPath").Any())
{
continue;
}
project.Descendants("Compile")
.ShouldNotContain(element =>
(string?)element.Attribute("Remove") == generatedFilesPattern,
projectFile);
}
}
[Fact]
public void Desktop_Project_Does_Not_Keep_Stale_Window_Directory_Exclusions()
{
var project = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/AtomUI.Desktop.Controls.csproj"));
project.ShouldNotContain("Window\\Reflection");
project.ShouldNotContain("Window\\Visuals");
}
[Fact]
public void NuGet_Generator_Entry_Points_Import_Flat_Feature_Files()
{