MSBuild
MSBuild 是 Microsoft 的构建平台,用于编译、测试和部署 .NET 项目。.csproj 项目文件本质上是 MSBuild 的 XML 脚本,定义了构建过程的各个阶段和目标。现代 .NET SDK 风格的项目大大简化了 MSBuild 配置,但理解 MSBuild 的工作原理对于高级场景仍然必要。
项目文件格式
.NET 项目文件使用 XML 格式,根元素是 Project。传统项目文件使用复杂的 XML 结构,现代 SDK 风格项目只需要几个属性。
<!-- 传统项目文件 (复杂) -->
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{12345678-1234-1234-1234-123456789ABC}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Compile Include="Program.cs" />
</ItemGroup>
</Project>
<!-- 现代 SDK 风格项目 (简洁) -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>SDK 风格项目隐式引入了常用的目标和属性,大大简化了配置。Sdk 属性指定了使用的 SDK,Microsoft.NET.Sdk 是最常用的 SDK,适用于大多数项目类型。
目标和任务
MSBuild 的核心概念是目标 (Target) 和任务 (Task)。目标是一组任务的集合,任务是执行特定操作的单元。
<Project Sdk="Microsoft.NET.Sdk">
<!-- 自定义目标 -->
<Target Name="PrintInfo" BeforeTargets="Build">
<Message Text="Building $(ProjectName)" Importance="high" />
</Target>
<!-- 任务调用 -->
<Target Name="CustomTask" AfterTargets="Build">
<Copy SourceFiles="$(OutputPath)$(AssemblyName).dll"
DestinationFolder="../Bin/" />
</Target>
</Project>常用的内置任务包括 Copy(复制文件)、Delete(删除文件)、Message(输出消息)、Exec(执行命令)。目标之间可以定义依赖关系,BeforeTargets 和 AfterTargets 属性指定目标的执行顺序。
属性和项
MSBuild 使用属性 (Property) 和项 (Item) 来管理配置和数据。属性是键值对,项是一组文件的集合。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>1.0.0</Version>
<Authors>John Doe</Authors>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="**/*.cs" />
<EmbeddedResource Include="**/*.resx" />
<None Include="**/*.txt" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>属性可以使用 $(PropertyName) 语法引用,支持属性组合和条件判断。项可以使用 Include、Exclude、Update、Remove 等操作进行过滤和修改。
构建配置
MSBuild 支持多个构建配置,常见的有 Debug 和 Release。配置决定了编译器的优化选项、调试信息的生成方式等。
# Debug 构建
dotnet build -c Debug
# Release 构建
dotnet build -c Release
# 自定义配置
dotnet build -c:CustomConfigDebug 配置禁用优化、生成完整的调试信息、定义 DEBUG 符号。Release 配置启用优化、减少调试信息、不定义 DEBUG 符号。
多目标框架
项目可以同时支持多个目标框架,使用 TargetFrameworks (复数) 属性指定。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net48</TargetFrameworks>
</PropertyGroup>
<!-- 条件引用 -->
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
<Reference Include="System.Net.Http" />
</ItemGroup>
</Project>多目标项目会针对每个框架编译一次,生成多个输出目录。
条件编译
可以使用条件编译符号在编译时选择性地包含或排除代码。
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>RELEASE</DefineConstants>
</PropertyGroup>#if DEBUG
Console.WriteLine("Debug mode");
#elif RELEASE
Console.WriteLine("Release mode");
#else
Console.WriteLine("Other mode");
#endif依赖关系
项目之间可以定义依赖关系,被依赖的项目会先编译。
<ItemGroup>
<!-- 项目引用 -->
<ProjectReference Include="..\MyProject\MyProject.csproj" />
<!-- 程序集引用 -->
<Reference Include="System.Data.SqlClient" />
</ItemGroup>项目引用会自动处理依赖传递,如果被引用的项目引用了其他项目,这些依赖也会被解析。
自定义目标
开发者可以编写自定义目标和任务来扩展构建过程。Task 是继承自 Task 类的 C# 类,使用 Output 属性定义输出。
// 自定义任务
public class CustomTask : Task
{
[Required]
public string InputFile { get; set; }
[Output]
public string OutputFile { get; set; }
public override bool Execute()
{
// 执行任务逻辑
OutputFile = ProcessFile(InputFile);
return true;
}
}<!-- 使用自定义任务 -->
<Project>
<UsingTask TaskName="CustomTask" AssemblyFile="CustomTasks.dll" />
<Target Name="UseCustomTask">
<CustomTask InputFile="input.txt">
<Output TaskParameter="OutputFile" PropertyName="Result" />
</CustomTask>
<Message Text="Result: $(Result)" />
</Target>
</Project>dotnet build 命令
dotnet build 是常用的构建命令,支持多种选项。
# 基本构建
dotnet build
# 指定配置
dotnet build -c Release
# 指定目标框架
dotnet build -f net6.0
# 指定输出目录
dotnet build -o ./bin/Release
# 不还原依赖
dotnet build --no-restore
# 生成详细日志
dotnet build -v detailed
# 并行构建
dotnet build -mdotnet build 内部调用 MSBuild,大部分 MSBuild 命令也可以通过 dotnet msbuild 直接调用。
目录.build.props
自定义的 Directory.Build.props 文件可以统一配置多个项目的构建选项,避免在每个项目中重复配置。
<!-- Directory.Build.props (放在解决方案根目录) -->
<Project>
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>Directory.Build.props 会被所有项目自动导入,适合配置代码分析、版本号、公司信息等全局设置。