其他
审查和清理 MSBuild 项目/构建文件时…
当你审查 .csproj/.vbproj/.fsproj/.props/.targets/.proj 或 Directory.Build.props/.targets,或排查由项目文件写法导致的隐蔽构建问题时,用这份反模式目录逐项检查并修复。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 39/100 ×0.15
适用场景
审查和清理 MSBuild 项目/构建文特定
解决思路
- 按 AP-01 到 AP-15 逐项扫描 .csproj/.vbproj/.fsproj/.props/.targets/.proj 或 Directory.Build.props/.targets;每项先按 Smell 定位,再按 BAD→GOOD 的 Fix 转换。
- AP-01:将 <Exec Command='mkdir ...' />、copy/cp、del/rm、move/mv、echo text > file、touch、xcopy /s 分别替换为内置任务 <MakeDir>、<Copy>、<Delete>、<Move>、<WriteLinesToFile>、<Touch>、带 item globs 的 <Copy>;避免用 <Exec> 执行这些操作。
- AP-02:检查所有 == 和 != 比较;将 Condition 中比较两侧都用单引号包裹,例如写成 '$(Configuration)' == 'Release',不要写 $(Configuration) == Release。
- AP-03:删除硬编码绝对路径(如 C:\tools\、D:\packages\、/usr/local/bin/);改用 $(MSBuildThisFileDirectory)、$(MSBuildProjectDirectory)、$([MSBuild]::GetDirectoryNameOfFileAbove(...))、$([MSBuild]::NormalizePath(...));<Import> 使用 $(RepoRoot)eng\common.props 等可重定位路径。
- AP-04:删除与 .NET SDK 默认值相同的属性:OutputType=Library、EnableDefaultItems=true、EnableDefaultCompileItems=true、RootNamespace/AssemblyName 与项目名相同、AppendTargetFrameworkToOutputPath=true;只保留真正非默认值,如 TargetFramework。
- AP-05:在 SDK-style 项目中删除手动的 <Compile Include='...' /> 列表,因为 SDK 默认 glob **/*.cs;只保留需要排除的 <Compile Remove='...' /> 或 Exclude;非 SDK-style 遗留项目仍需显式列出文件;F# .fsproj 必须按依赖顺序显式列出所有源文件(utility/leaf 模块在上,Program.fs 入口在下),.fsi 签名文件必须紧挨在其 .fs 实现文件之前。
- AP-06:把 NuGet 包的 <Reference Include='...' HintPath='..\packages\...' /> 改为 <PackageReference Include='...' Version='...' />;仅对 .NET Framework GAC 程序集(如 WindowsBase、PresentationCore)保留无 HintPath 的 <Reference>。
- AP-07:对 analyzer 和构建工具包设置 PrivateAssets='all',例如 StyleCop.Analyzers;参考 references/private-assets.md 查看 BAD/GOOD 示例和需要此设置的完整包列表。
- AP-08:当同一 <PropertyGroup> 在 3+ 个项目文件中重复时,把它移到 repo/src 根目录的 Directory.Build.props 中定义一次;参考 directory-build-organization 技能组织 Directory.Build.props/.targets。
- AP-09:当同一包在不同项目中出现版本漂移时,采用 Central Package Management 集中管理包版本,避免每个项目单独指定并漂移;细节见 https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management。
- AP-10:把 50+ 行、做多件不相关事情的单个 <Target> 拆成单一职责的多个 Target;用 BeforeTargets/AfterTargets/DependsOnTargets/Condition 组合;对可增量步骤设置 Inputs/Outputs;例如拆为 WriteVersionFile、CopyLicense、SignAssemblies,Copy 时用 SkipUnchangedFiles='true'。
- AP-11:给自定义 <Target> 添加 Inputs 和 Outputs 属性以支持增量构建,避免每次构建都运行;注册 FileWrites;参考 references/incremental-build-inputs-outputs.md 和 incremental-build 技能。
- AP-12:把默认值放到 .props 文件(早导入),把构建逻辑和 Target 放到 .targets 文件(晚导入);在 .props 中用条件设置默认值,例如 MyToolVersion 仅在为空时设为 2.0(Condition 使用 '$(MyToolVersion)' == '');不要在 .targets 中设置默认值。
- AP-13:对可选或环境相关的 <Import> 加 Exists('...') 守卫;对构建必需的 Import 不加守卫以快速失败;对 NuGet 包 build/ 或 buildTransitive/ 内的转发 import,先解析 packed layout:读取项目目录及其直接父目录中的 *.nuspec,以及 .csproj 中 <None>/<Content> 的 <PackagePath>,只有目标路径在源码树和投影包布局都缺失时才标记;buildTransitive/ → build/ 转发应通过同级 build/*.props 或 build/*.targets,不要直接到 buildMultiTargeting/;当 build/ 按 TFM 时,用文件所在文件夹推导 TFM 段而不是 $(TargetFramework),避免 MSB4019;参考 dotnet-msbuild/extension-points 技能的 Source tree vs packed layout 和 Forwarding chain。
- AP-14:区分反斜杠路径的真错误和风格问题:在 <Exec Command> 原始 shell 字符串、CDATA 块、<WriteLinesToFile> 写入的源码、给非 MSBuild 消费者(自定义脚本、response files、环境变量)的路径、直接调用 OS 文件 API 的自定义任务中,避免反斜杠;在 <Import Project>、内置任务文件属性、item Include/Exclude globs 中反斜杠会被 MSBuild evaluator 在 Unix 上规范化,不算错误;标记为错误前先问该字符串是否经过 MSBuild evaluator 还是直接交给非 MSBuild 消费者;新代码推荐正斜杠;注意 $(MSBuildThisFileDirectory) 已带平台合适分隔符,所以 $(MSBuildThisFileDirectory)tools/mytool 跨平台可用。
- AP-15:当同一属性在 Directory.Build.props 和 .csproj 中无条件设置且最后写入静默生效时,改为条件设置默认值,使覆盖有意识且可追踪;例如在 Directory.Build.props 中仅在 OutputPath 为空时设为 bin\custom\(Condition 使用 '$(OutputPath)' == ''),让 .csproj 可有意覆盖或保留默认值。
- 补充:如仍有未覆盖的反模式,查阅 references/additional-antipatterns.md 中 AP-16 至 AP-23 以及快速检查清单。
适用边界 · 注意事项
- 不适用于非 MSBuild 构建系统(如 npm、Maven、CMake)。
- 若目标是迁移到 SDK-style,不要用本目录,应使用 msbuild-modernization。
- 不要对非 SDK-style(legacy)项目或 F# .fsproj 应用 AP-05 中删除显式 <Compile Include> 的建议。