.vcxproj.props 文件结构

MSBuild 是 Visual Studio 中默认的项目系统;在 Visual C++ 中选择“文件”>“新建项目”会创建一个 MSBuild 项目,其设置存储于扩展名为 .vcxproj 的 XML 项目文件中。 项目文件还可以导入 .props 文件和 .targets 文件,这两种文件能存储设置。

如果打算在 IDE 中维护项目属性,建议仅在 IDE 中创建和修改 .vcxproj 项目,并且不要手动编辑文件。 在大多数情况下,完全不需要手动编辑项目文件。 手动编辑可能会中断修改 Visual Studio 属性页中的项目设置所需的项目连接,并且可能导致难以调试和修复的生成错误。 有关使用属性页的详细信息,请参阅在 Visual Studio 中设置 C++ 编译器和生成属性

在一定规模上,在 IDE 中管理许多单个项目变得繁琐且容易出错。 很难在数十个或数百个项目之间维持一致性或强制实施标准化。 在这些情况下,有必要编辑项目文件,以便跨多个项目为常规属性使用自定义的 .props.targets 文件。 如果需要无法在 IDE 中实现的自定义,也可使用这些文件。 Directory.Build.propsDirectory.Build.targets 文件是插入自定义项的好位置,这些文件会自动导入到所有基于 MSBuild 的项目中。

在某些情况下,只用自定义的 .props.targets 文件可能没法满足你的项目管理需求。 你仍可能需要手动修改 .vcxproj 项目文件或属性表。 要进行手动编辑,需要很好地了解 MSBuild,还必须遵循本文中的准则。 为使 IDE 能够自动加载和更新 .vcxproj 文件,这些文件存在一些不适用于其他 MSBuild 项目文件的限制。 出现失误可能会导致 IDE 崩溃或出现意外行为。

对于手动编辑方案,本文包含了有关 .vcxproj 结构和相关文件的基本信息。

重要考虑因素

如果你选择手动编辑 .vcxproj 文件,请注意以下事项:

  • 文件结构必须遵循本文中所述的规定形式。

  • Visual Studio C++ 项目系统目前不支持直接在项目项中使用通配符或列表。 例如,不支持以下形式:

    <ItemGroup>
       <None Include="*.txt"/>
       <ClCompile Include="a.cpp;b.cpp"/>
    </ItemGroup>
    

    有关项目中通配符支持和可能的解决方法的详细信息,请参阅 .vcxproj 文件和通配符

  • Visual Studio C++ 项目系统目前不支持在项目项路径中使用宏。 例如,不支持以下形式:

    <ItemGroup>
       <ClCompile Include="$(IntDir)\generated.cpp"/>
    </ItemGroup>
    

    “不支持”表示不能保证宏适用于 IDE 中的所有操作。 未在不同配置中更改其值的宏应正常工作,但如果将某项移动到不同筛选器或项目,则可能不会保留宏。 为不同的配置更改其值的宏会导致问题。 IDE 不期望项目项配置因不同的项目配置而不同。

  • 若要在“项目属性”对话框中编辑项目属性时正确添加、删除或修改这些属性,该文件必须为每个项目配置包含单独的组。 条件必须采用以下形式:

    Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"
    
  • 分组中指定的每个属性必须带有正确的标签,就像项目规则文件中所指定的那样。 有关详细信息,请参阅属性页 XML 规则文件

.vcxproj 文件元素

可以通过使用任何文本或 XML 编辑器来检查 .vcxproj 文件的内容。 若要在 Visual Studio 中进行查看,请在解决方案资源管理器中右键单击项目,选择“卸载项目”,再选择“编辑 Foo.vcxproj”

要注意的第一点是顶级元素按特定顺序显示。 例如:

  • 大多数的属性组和项定义组都在导入 Microsoft.Cpp.Default.props 后出现。

  • 所有目标都在文件末尾导入。

  • 存在多个属性组,每个都带有唯一标签并按特定顺序显示。

由于 MSBuild 基于按顺序的计算模型,所以项目文件中的元素顺序至关重要。 如果项目文件(包括导入的所有 .props.targets 文件)包含多个属性定义,则最新的定义会重写前面的定义。 在以下示例中,在编译期间会设置值“xyz”,因为它是 MSBuild 引擎在执行计算期间最后遇到的值。

  <MyProperty>abc</MyProperty>
  <MyProperty>xyz</MyProperty>

以下代码片段演示了一个极简的 .vcxproj 文件。 Visual Studio 生成的任何 .vcxproj 文件将包含这些顶级 MSBuild 元素。 此外,它们将按此顺序出现,不过它们可能包含每个此类顶级元素的多个副本。 任何 Label 特性只是 Visual Studio 用作编辑标志的任意标记;它们不具备其他功能。

<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
  <ItemGroup Label="ProjectConfigurations" />
  <PropertyGroup Label="Globals" />
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.default.props" />
  <PropertyGroup Label="Configuration" />
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings" />
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup />
  <ItemGroup />
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets" />
</Project>

以下部分介绍其中每个元素的用途以及它们如此排序的原因:

Project 元素

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

Project 为根节点。 它表明要使用的 MSBuild 版本以及将文件向 MSBuild.exe 传递时要执行的默认目标。

ProjectConfigurations ItemGroup 元素

<ItemGroup Label="ProjectConfigurations" />

ProjectConfigurations 包含项目配置描述。 例如“Debug|Win32”、“Release|Win32”和“Debug|ARM”等等。 很多项目设置都特定于给定的配置。 例如,你可能想要为发布生成而不是调试生成设置优化属性。

在生成时不使用 ProjectConfigurations 项组。 Visual Studio IDE 需要将其用于加载项目。 可将这个项组移至 .props 文件并导入至 .vcxproj 文件。 但是在这种情况下,如果需要添加或删除配置,则需要手动编辑 .props 文件而无法使用 IDE。

ProjectConfiguration 元素

下面的代码片段演示一个项目配置。 此示例中的配置名称为“Debug|x64”。 项目配置名称必须采用 $(Configuration)|$(Platform) 格式。 ProjectConfiguration 节点可包含两个属性:ConfigurationPlatform。 当配置处于活动状态时,将使用此处指定的值自动设置这些属性。

<ProjectConfiguration Include="Debug|x64">
  <Configuration>Debug</Configuration>
  <Platform>x64</Platform>
</ProjectConfiguration>

IDE 预期为所有 ProjectConfiguration 项中使用的 ConfigurationPlatform 值的任意组合查找项目配置。 这通常意味着项目可能具有无意义的项目配置以满足此要求。 例如,如果项目具有这些配置:

  • Debug|Win32

  • Retail|Win32

  • Special 32-bit Optimization|Win32

那么即使“Special 32-bit Optimization”对于 x64 是没有意义的,项目还是需要这些配置:

  • 调试|x64

  • Retail|x64

  • Special 32-bit Optimization|x64

可以在“解决方案配置管理器”中禁用任何配置的生成和部署命令

Globals PropertyGroup 元素

<PropertyGroup Label="Globals" />

Globals 包含项目级设置,例如 ProjectGuidRootNamespaceApplicationTypeApplicationTypeRevision。 最后两项通常定义目标操作系统。 一个项目只能针对一个操作系统,因为目前引用和项目项不能有条件。 通常不会在项目文件中的其他位置重写这些属性。 此组与配置无关,通常项目文件中仅存在一个 Globals 组。

Microsoft.Cpp.default.props Import 元素

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.default.props" />

Microsoft.Cpp.default.props 属性表是 Visual Studio 附带的,且无法修改。 它包含项目的默认设置。 根据 ApplicationType,可能默认值会有所不同。

Configuration PropertyGroup 元素

<PropertyGroup Label="Configuration" />

Configuration 属性组具备附加的配置条件(例如 Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"),并在多个副本中出现(每个配置各一个)。 此属性组承载着为特定配置设置的属性。 配置属性包括 PlatformToolset,并控制 Microsoft.Cpp.props 中的系统属性表所包含的内容。 例如,如果定义 <CharacterSet>Unicode</CharacterSet> 属性,则会包含系统属性表 microsoft.Cpp.unicodesupport.props。 如果检查 Microsoft.Cpp.props,你会看到该行:<Import Condition="'$(CharacterSet)' == 'Unicode'" Project="$(VCTargetsPath)\microsoft.Cpp.unicodesupport.props" />

Microsoft.Cpp.props Import 元素

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />

Microsoft.Cpp.props 属性表为许多特定于工具的属性(直接或通过导入)定义默认值。 示例包括编译器的优化和警告级别属性、MIDL 工具的 TypeLibraryName 属性,等等。 它还基于紧靠在其前面的属性组中定义的配置属性导入各种系统属性表。

ExtensionSettings ImportGroup 元素

<ImportGroup Label="ExtensionSettings" />

ExtensionSettings 组包含属于生成自定义项的属性表的导入。 生成自定义最多由三个文件定义:.targets 文件、.props 文件和 .xml 文件。 此导入组包含 .props 文件的导入。

PropertySheets ImportGroup 元素

<ImportGroup Label="PropertySheets" />

PropertySheets 组包含用户属性表的导入。 这些导入是在 Visual Studio 中通过属性管理器视图添加的属性表。 这些导入的列出顺序很重要,且会反映在属性管理器中。 项目文件通常包含此类导入组的多个实例,每个项目配置各一个。

UserMacros PropertyGroup 元素

<PropertyGroup Label="UserMacros" />

UserMacros 包含创建为变量的属性,这些属性用于自定义生成过程。 例径定如,可定义将自定义输出路义为 $(CustomOutputPath) 的用户宏,并用它来定义其他变量。 此属性组包含这类属性。 在 Visual Studio 中不会将此组填充到项目文件,因为 Visual C++ 不支持在配置中使用用户宏。 属性表支持用户宏。

按配置 PropertyGroup 元素

<PropertyGroup />

此属性组有多个实例,所有项目配置都各有一个。 每个属性组必须具备一个附加的配置条件。 如果漏掉了任何配置,“项目属性”对话框将不会正常工作。 与前面列出的属性组不同,此属性组没有标签。 此组包含项目配置级别的设置。 这些设置适用于所有属于该特定项组的文件。 在此处初始化生成自定义项定义元数据。

PropertyGroup 必须位于 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> 之后,并且在它之前不能存在其他不带标签的 PropertyGroup(否则项目属性的编辑工作将无法正确进行)。

按配置 ItemDefinitionGroup 元素

<ItemDefinitionGroup />

包含项定义。 这些定义必须和无标签的按配置 PropertyGroup 元素遵循同样的条件规则。

ItemGroup 元素

<ItemGroup />

ItemGroup 元素包含项目中的项(源文件等)。 不支持对项目项(也就是根据规则定义被视为项目项的项类型)使用条件。

元数据应具备每个配置的配置条件,即使它们都是一样的。 例如:

<ItemGroup>
  <ClCompile Include="stdafx.cpp">
    <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatWarningAsError>
    <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatWarningAsError>
  </ClCompile>
</ItemGroup>

Visual Studio C++ 项目系统目前不支持在项目项中使用通配符。

<ItemGroup>
  <ClCompile Include="*.cpp"> <!--Error-->
</ItemGroup>

Visual Studio C++ 项目系统目前不支持在项目项中使用宏。

<ItemGroup>
  <ClCompile Include="$(IntDir)\generated.cpp"> <!--not guaranteed to work in all scenarios-->
</ItemGroup>

ItemGroup 中指定了引用,且这些引用具有以下限制:

  • 引用不支持条件。

  • 引用元数据不支持条件。

Microsoft.Cpp.targets Import 元素

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

(直接或通过导入)定义 C++ 目标,例如生成、清理等。

ExtensionTargets ImportGroup 元素

<ImportGroup Label="ExtensionTargets" />

此组包含生成自定义项目标文件的导入。

排序错误的后果

Visual Studio IDE 依赖于按上述顺序排列的项目文件。 例如,当你在属性页中定义属性值时,IDE 通常会将属性定义放置在带有空标签的属性组中。 这种排序确保用户定义的值可以重写系统属性表中的默认值。 同样,在末尾导入目标文件,因为它们使用前面定义的属性而通常不会自己定义属性。 类似地,在系统属性表之后导入用户属性表(通过 Microsoft.Cpp.props 包含)。 此顺序确保用户可以重写系统属性表中的默认值。

如果 .vcxproj 文件不遵循此布局,可能会生成与预期不同的结果。 例如,如果系统属性表错误地导入在用户定义的属性表之后,那么用户设置会被系统属性表重写。

即使是 IDE 设计时体验,也在一定程度上依赖于元素的正确排序。 例如,如果 .vcxproj 文件没有 PropertySheets 导入组,IDE 可能无法确定放置用户在“属性管理器”中创建的新属性表的位置。 这可能会导致系统表重写用户表。 虽然 IDE 所使用的启发式方法可以容忍 .vcxproj 文件布局中细微的不一致,但我们强烈建议不要偏离本文前面部分所述的结构。

IDE 如何使用元素标签

在 IDE 中的常规属性页中设置 UseOfAtl 属性时,该属性将写入项目文件中的配置属性组。 同一属性页中的 TargetName 属性将写入无标签的按配置属性组。 Visual Studio 会在属性页的 xml 文件中查找关于属性写入位置的信息。 对于常规属性页(假设你有英文版的 Visual Studio 2019 Enterprise Edition),该文件则为 %ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\VCTargets\1033\general.xml。 属性页 XML 规则文件定义关于 Rule 及其所有属性的静态信息。 这类信息之一就是 Rule 属性在目标文件(也就是将要写入值的文件)中的首选位置。 首选位置由项目文件元素上的 Label 特性指定。

属性表布局

以下 XML 代码片段是属性表 (.props) 文件的最简布局。 这与 .vcxproj 文件类似,并且可以从前面的讨论推断出 .props 元素的功能。

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup />
  <ItemGroup />
</Project>

若要制作自己的属性表,请复制 VCTargets 文件夹中的某个 .props 文件,并根据需要对它进行修改。 对于 Visual Studio 2019 Enterprise Edition,默认的 VCTargets 路径为 %ProgramFiles%\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\VCTargets

另请参阅

在 Visual Studio 中设置 C++ 编译器并生成属性
属性页 XML 文件
.vcxproj 文件和通配符