了解项目文件

作者 :Jason Lee

Microsoft 生成引擎 (MSBuild) 项目文件是生成和部署过程的核心。 本主题从 MSBuild 和项目文件的概念性概述开始。 它描述了使用项目文件时会遇到的关键组件,并介绍了如何使用项目文件部署实际应用程序的示例。

学习内容:

  • MSBuild 如何使用 MSBuild 项目文件来生成项目。
  • MSBuild 如何与部署技术(例如 Internet Information Services (IIS) Web 部署工具 (Web 部署) 集成。
  • 如何了解项目文件的关键组件。
  • 如何使用项目文件来生成和部署复杂的应用程序。

MSBuild 和项目文件

在 Visual Studio 中创建和生成解决方案时,Visual Studio 使用 MSBuild 生成解决方案中的每个项目。 每个 Visual Studio 项目都包含一个 MSBuild 项目文件,其中包含反映项目类型的文件扩展名,例如,C# 项目 (.csproj) 、Visual Basic.NET 项目 (.vbproj) 或数据库项目 (.dbproj) 。 为了生成项目,MSBuild 必须处理与项目关联的项目文件。 项目文件是一个 XML 文档,其中包含 MSBuild 生成项目所需的所有信息和说明,例如要包含的内容、平台要求、版本控制信息、Web 服务器或数据库服务器设置以及必须执行的任务。

MSBuild 项目文件基于 MSBuild XML 架构,因此生成过程是完全开放和透明的。 此外,无需安装 Visual Studio 即可使用 MSBuild 引擎,MSBuild.exe 可执行文件是.NET Framework的一部分,可以从命令提示符运行它。 开发人员可以使用 MSBuild XML 架构创建自己的 MSBuild 项目文件,以对项目的生成和部署方式实施复杂且精细的控制。 这些自定义项目文件的工作方式与 Visual Studio 自动生成的项目文件完全相同。

注意

还可以将 MSBuild 项目文件与 Team Foundation Server 中的 Team Build 服务一起使用, (TFS) 。 例如,可以在持续集成 (CI) 方案中使用项目文件,以便在签入新代码时自动部署到测试环境。 有关详细信息,请参阅 为自动 Web 部署配置 Team Foundation Server

项目文件命名约定

创建自己的项目文件时,可以使用喜欢的任何文件扩展名。 但是,为了使解决方案更易于其他人理解,应使用以下常见约定:

  • 创建生成项目的项目文件时,请使用 .proj 扩展名。
  • 创建可重用项目文件以导入到其他项目文件时,请使用 .targets 扩展名。 扩展名为 .targets 的文件通常不会自行生成任何内容,它们仅包含可导入到 .proj 文件中的说明。

与部署技术的集成

如果你在 Visual Studio 2010 中使用过 Web 应用程序项目(如 ASP.NET Web 应用程序和 ASP.NET MVC Web 应用程序),你将知道这些项目包括对打包 Web 应用程序并将其部署到目标环境的内置支持。 这些项目 的“属性” 页包括 “打包/发布 Web ”和“ 包/发布 SQL ”选项卡,可用于配置应用程序的组件的打包和部署方式。 此时会显示 “打包/发布 Web ”选项卡:

“打包/发布 Web”选项卡

这些功能背后的基础技术称为 Web 发布管道 (WPP) 。 WPP 实质上将 MSBuild 和 Web 部署 结合在一起,为 Web 应用程序提供完整的生成、包和部署过程。

好消息是,在为 Web 项目创建自定义项目文件时,可以利用 WPP 提供的集成点。 可以在项目文件中包含部署说明,这样就可以生成项目、创建 Web 部署包,并通过单个项目文件和单个调用 MSBuild 在远程服务器上安装这些包。 还可以在生成过程中调用任何其他可执行文件。 例如,可以运行 VSDBCMD.exe 命令行工具来从架构文件部署数据库。 在本主题中,你将了解如何利用这些功能来满足企业部署方案的要求。

注意

有关 Web 应用程序部署过程的工作原理的详细信息,请参阅 ASP.NET Web 应用程序项目部署概述

项目文件的剖析

在更详细地查看生成过程之前,需要花点时间熟悉 MSBuild 项目文件的基本结构。 本部分概述了查看、编辑或创建项目文件时将遇到的更常见元素。 具体而言,你将了解:

  • 如何使用 属性 来管理生成过程的变量。
  • 如何使用 来标识生成过程的输入,如代码文件。
  • 如何使用目标和任务,使用项目文件中其他位置定义的属性向 MSBuild 提供执行说明。

这显示了 MSBuild 项目文件中关键元素之间的关系:

MSBuild 项目文件中关键元素之间的关系。

Project 元素

Project 元素是每个项目文件的根元素。 除了标识项目文件的 XML 架构外, Project 元素还可以包含用于指定生成过程的入口点的属性。 例如,在 Contact Manager 示例解决方案中, Publish.proj 文件指定生成应通过调用名为 FullPublish 的目标开始。

<Project ToolsVersion="4.0" DefaultTargets="FullPublish" 
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  
</Project>

属性和条件

项目文件通常需要提供许多不同的信息片段才能成功生成和部署项目。 这些信息片段可能包括服务器名称、连接字符串、凭据、生成配置、源和目标文件路径,以及要包含以支持自定义的任何其他信息。 在项目文件中,必须在 PropertyGroup 元素中定义属性。 MSBuild 属性由键值对组成。 在 PropertyGroup 元素中,元素名称定义属性键,元素的内容定义属性值。 例如,可以定义名为 ServerNameConnectionString 的属性来存储静态服务器名称和连接字符串。

<PropertyGroup>    
   <ServerName>FABRIKAM\TEST1</ServerName>
   <ConnectionString>
     Data Source=FABRIKAM\TESTDB;InitialCatalog=ContactManager,...
   </ConnectionString>
</PropertyGroup>

若要检索属性值,请使用 格式 $ (PropertyName) 。 例如,若要检索 ServerName 属性的值,请键入:

$(ServerName)

注意

本主题稍后将介绍如何以及何时使用属性值的示例。

将信息作为静态属性嵌入到项目文件中并不总是管理生成过程的理想方法。 在很多情况下,你需要从其他源获取信息,或者让用户能够从命令提示符中提供信息。 MSBuild 允许将任何属性值指定为命令行参数。 例如,当用户从命令行运行 MSBuild.exe 时,可以为 ServerName 提供值。

msbuild.exe Publish.proj /p:ServerName=FABRIKAM\TESTWEB1

注意

有关可与 MSBuild.exe 一起使用的参数和开关的详细信息,请参阅 MSBuild 命令行参考

可以使用相同的属性语法来获取环境变量和内置项目属性的值。 已为你定义许多常用属性,可以通过包括相关参数名称在项目文件中使用这些属性。 例如,若要检索当前项目平台(例如 x86AnyCpu),可以在项目文件中包含 $ (Platform) 属性引用。 有关详细信息,请参阅 生成命令和属性的宏通用 MSBuild 项目属性保留属性

属性通常与 条件一起使用。 大多数 MSBuild 元素都支持 Condition 属性,这使你可以指定 MSBuild 评估元素的条件。 例如,请考虑此属性定义:

<PropertyGroup>
   <OutputRoot Condition=" '$(OutputRoot)'=='' ">..\Publish\Out\</OutputRoot>
   ...
</PropertyGroup>

MSBuild 处理此属性定义时,首先会检查 $ (OutputRoot) 属性值是否可用。 如果属性值为空(换句话说,用户尚未为此属性提供值),则条件的计算结果为 true ,并将属性值设置为 。。\Publish\Out。如果用户为此属性提供了值,则条件的计算结果为 false ,并且不使用静态属性值。

有关指定条件的不同方式的详细信息,请参阅 MSBuild 条件

项和项组

项目文件的一个重要角色是定义生成过程的输入。 通常,这些输入是文件-代码文件、配置文件、命令文件以及生成过程中需要处理或复制的任何其他文件。 在 MSBuild 项目架构中,这些输入由 Item 元素表示。 在项目文件中,必须在 ItemGroup 元素中定义项。 与 Property 元素一样,你可以以自己喜欢的方式命名 Item 元素。 但是,必须指定 Include 属性来标识项表示的文件或通配符。

<ItemGroup>
   <ProjectsToBuild Include="$(SourceRoot)ContactManager-WCF.sln"/>
</ItemGroup>

通过指定多个具有相同名称的 Item 元素,可以有效地创建一个命名的资源列表。 了解此操作的一个好方法是查看 Visual Studio 创建的某个项目文件。 例如,示例解决方案中的 ContactManager.Mvc.csproj 文件包含许多项组,每个组都有几个同名 的 Item 元素。

<ItemGroup>
   <Reference Include="Microsoft.CSharp" />
   <Reference Include="System.Runtime.Serialization" />
   <Reference Include="System.ServiceModel" />
   ...
</ItemGroup>
<ItemGroup>
   <Compile Include="Controllers\AccountController.cs" />
   <Compile Include="Controllers\ContactsController.cs" />
   <Compile Include="Controllers\HomeController.cs" />
   ...
</ItemGroup>
<ItemGroup>
   <Content Include="Content\Custom.css" />
   <Content Include="CreateDatabase.sql" />
   <Content Include="DropDatabase.sql" />
   ...
</ItemGroup>

这样,项目文件将指示 MSBuild 构造需要以相同方式处理的文件列表- “引用 ”列表包含必须就位的程序集才能成功生成, “编译 ”列表包含必须编译的代码文件,而 “内容 ”列表包含必须以无更改方式复制的资源。 本主题稍后将介绍生成过程如何引用和使用这些项。

Item 元素还可以包含 ItemMetadata 子元素。 这些是用户定义的键值对,实质上表示特定于该项的属性。 例如,项目文件中的许多 Compile 项元素包括 DependentUpon 子元素。

<Compile Include="Global.asax.cs">
   <DependentUpon>Global.asax</DependentUpon>
</Compile>

注意

除了用户创建的项元数据外,所有项在创建时都会分配各种通用元数据。 有关详细信息,请参阅 已知项元数据

可以在根级 Project 元素或特定 Target 元素中创建 ItemGroup 元素。 ItemGroup 元素还支持 Condition 属性,使你可以根据项目配置或平台等条件定制生成过程的输入。

目标和任务

在 MSBuild 架构中, Task 元素表示单个生成指令 (或任务) 。 MSBuild 包括大量预定义任务。 例如:

  • 复制任务将文件复制到新位置。
  • Csc 任务调用 Visual C# 编译器。
  • Vbc 任务调用 Visual Basic 编译器。
  • Exec 任务运行指定的程序。
  • 消息任务将消息写入记录器。

注意

有关现成可用的任务的完整详细信息,请参阅 MSBuild 任务参考。 有关任务的详细信息,包括如何创建自己的自定义任务,请参阅 MSBuild 任务

任务必须始终包含在 Target 元素中。 Target 元素是一组按顺序执行的一个或多个任务,项目文件可以包含多个目标。 如果要运行一个任务或一组任务,请调用包含它们的目标。 例如,假设你有一个记录消息的简单项目文件。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Target Name="LogMessage">
      <Message Text="Hello world!" />
   </Target>
</Project>

可以使用 /t 开关指定目标,从命令行调用目标。

msbuild.exe Publish.proj /t:LogMessage

或者,可以将 DefaultTargets 属性添加到 Project 元素,以指定要调用的目标。

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
         DefaultTargets="FullPublish">
   <Target Name="LogMessage">
      <Message Text="Hello world!" />
   </Target>
</Project>

在这种情况下,无需从命令行指定目标。 只需指定项目文件,MSBuild 将调用 FullPublish 目标。

msbuild.exe Publish.proj

目标和任务都可以包括 Condition 属性。 因此,如果满足某些条件,可以选择忽略整个目标或单个任务。

一般来说,创建有用的任务和目标时,需要引用在项目文件中的其他位置定义的属性和项:

  • 若要使用属性值, 请键入 $ (PropertyName) ,其中 PropertyNameProperty 元素的名称或参数的名称。
  • 若要使用项,请键入 @ (ItemName) ,其中 ItemNameItem 元素的名称。

注意

请记住,如果创建具有相同名称的多个项,则会生成一个列表。 相反,如果创建具有相同名称的多个属性,则提供的最后一个属性值将覆盖以前具有相同名称的任何属性 - 一个属性只能包含一个值。

例如,在示例解决方案的 Publish.proj 文件中,查看 BuildProjects 目标。

<Target Name="BuildProjects" Condition=" '$(BuildingInTeamBuild)'!='true' ">
   <MSBuild Projects="@(ProjectsToBuild)"           
            Properties="OutDir=$(OutputRoot);
                        Configuration=$(Configuration);
                        DeployOnBuild=true;
                        DeployTarget=Package"
            Targets="Build" />
</Target>

在此示例中,可以观察以下要点:

  • 如果指定了 BuildingInTeamBuild 参数,并且其值为 true,则不会执行此目标内的任务。

  • 目标包含 MSBuild 任务的单个实例。 此任务允许生成其他 MSBuild 项目。

  • ProjectsToBuild 项将传递给任务。 此项可以表示项目或解决方案文件的列表,所有文件均由项组中的 ProjectsToBuild 项元素定义。 在这种情况下, ProjectsToBuild 项引用单个解决方案文件。

    <ItemGroup>
       <ProjectsToBuild Include="$(SourceRoot)ContactManager-WCF.sln"/>
    </ItemGroup>
    
  • 传递给 MSBuild 任务的属性值包括名为 OutputRootConfiguration 的参数。 如果提供了参数值,则这些值将设置为参数值;如果未提供,则设置为静态属性值。

    <PropertyGroup>
       ... 
       <Configuration Condition=" '$(Configuration)'=='' ">Release
       </Configuration>
       <OutputRoot Condition=" '$(OutputRoot)'=='' ">..\Publish\Out\
       </OutputRoot>
       ...
    </PropertyGroup>
    

还可以看到 MSBuild 任务调用名为 Build 的目标。 这是 Visual Studio 项目文件中广泛使用的几个内置目标之一,可在自定义项目文件中使用,例如 生成清理重新生成发布。 在本主题后面部分,你将详细了解如何使用目标和任务来控制生成过程,尤其是 MSBuild 任务。

注意

有关目标的详细信息,请参阅 MSBuild 目标

拆分项目文件以支持多个环境

假设你希望能够将解决方案部署到多个环境,例如测试服务器、过渡平台和生产环境。 这些环境之间的配置可能有很大差异,不仅在服务器名称、连接字符串等方面,还可能在凭据、安全设置和许多其他因素方面。 如果需要定期执行此操作,则每次切换目标环境时,在项目文件中编辑多个属性并不十分方便。 它也不是要求向生成过程提供无限属性值列表的理想解决方案。

幸运的是,有一个选择。 MSBuild 允许跨多个项目文件拆分生成配置。 若要查看其工作原理,请注意示例解决方案中有两个自定义项目文件:

  • Publish.proj,其中包含所有环境通用的属性、项和目标。
  • Env-Dev.proj,其中包含特定于开发人员环境的属性。

现在请注意, Publish.proj 文件包含一个 Import 元素,位于打开的 Project 标记的正下方。

<Import Project="$(TargetEnvPropsFile)"/>

Import 元素用于将另一个 MSBuild 项目文件的内容导入当前 MSBuild 项目文件。 在这种情况下, TargetEnvPropsFile 参数提供要导入的项目文件的文件名。 运行 MSBuild 时,可以为此参数提供值。

msbuild.exe Publish.proj /p:TargetEnvPropsFile=EnvConfig\Env-Dev.proj

这有效地将两个文件的内容合并到单个项目文件中。 使用此方法,可以创建一个包含通用生成配置的项目文件和包含特定于环境的属性的多个补充项目文件。 因此,只需运行具有不同参数值的命令即可将解决方案部署到其他环境。

运行具有不同参数值的命令可将解决方案部署到不同的环境。

以这种方式拆分项目文件是一种很好的做法。 它允许开发人员通过运行单个命令部署到多个环境,同时避免跨多个项目文件重复通用生成属性。

注意

有关如何为自己的服务器环境自定义特定于环境的项目文件的指南,请参阅 配置目标环境的部署属性

结论

本主题提供了 MSBuild 项目文件的一般介绍,并说明了如何创建自己的自定义项目文件来控制生成过程。 它还引入了将项目文件拆分为通用生成说明和环境特定生成属性的概念,以便轻松生成项目并将其部署到多个目标。

下一主题 了解生成过程,详细介绍了如何使用项目文件来控制生成和部署,方法是引导你完成具有实际复杂性的解决方案的部署。

深入阅读

有关项目文件和 WPP 的更深入介绍,请参阅 In the Microsoft 生成引擎: Using MSBuild and Team Foundation Build by Sayed Ibrahim Hashimi and William Bartholomew, ISBN: 978-0-7356-4524-0。