Visual Studio C++ Project 系统扩展性和工具集集成

Visual C++ 项目系统用于 .vcxproj 文件。 它基于 Visual Studio Common Project System (CPS),并提供额外的、C++特定扩展点,以便轻松集成新工具集、生成体系结构和目标平台。

C++ MSBuild 目标结构

所有.vcxproj文件都导入这些文件:

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

这些文件本身所定义的内容很少。 而是基于以下属性值导入其他文件:

  • $(ApplicationType)

    示例:Windows 应用商店、Android、Linux

  • $(ApplicationTypeRevision)

    这必须是一个有效的版本字符串,格式为 major.minor[.build[.revision]]。

    示例:1.0、10.0.0.0

  • $(Platform)

    出于历史原因,这一构建体系结构被命名为“平台”。

    示例:Win32、x86、x64、ARM

  • $(PlatformToolset)

    示例:v140、v141、v141_xp、llvm

这些属性值指定 $(VCTargetsPath) 根文件夹下的文件夹名称:

$(VCTargetsPath)\
    应用程序类型\
        $(ApplicationType)\
            $(ApplicationTypeRevision)\
                平台\
                    $(Platform)\
                        PlatformToolsets\
                            $(PlatformToolset)
    平台\
        $(Platform)\
            平台工具集\
                $(PlatformToolset)

对于 Windows 桌面项目,$(ApplicationType) 为空时,将使用 $(VCTargetsPath)\Platforms\ 文件夹。

添加新平台工具集

若要为现有 Win32 平台添加新的工具集“MyToolset”,请在 $(VCTargetsPath)\Platforms\Win32\PlatformToolsets\下创建 MyToolset 文件夹,并在其中创建 Toolset.propsToolset.targets 文件。

PlatformToolsets 下的每个文件夹名称 都将作为指定平台的可用 平台工具集 显示在 项目属性 对话框中,如下所示:

项目“属性页”对话框中的“平台工具集”属性

在该工具集支持的每个现有平台文件夹中,创建类似的 MyToolset 文件夹以及 Toolset.propsToolset.targets 文件。

添加新平台

若要添加新平台,例如“MyPlatform”,请在 $(VCTargetsPath)\Platforms\下创建一个 MyPlatform 文件夹,并创建 Platform.default.propsPlatform.propsPlatform.targets 文件。 此外,创建 $(VCTargetsPath)\Platforms\MyPlatform\PlatformToolsets\ 文件夹,并在其中创建至少一个工具集。

在每个 $(ApplicationType)$(ApplicationTypeRevision)Platforms 文件夹下的所有文件夹名称,都会在 IDE 中显示为项目可用的 Platform 选项。

“新建项目平台”对话框中的新平台选择

添加新的应用程序类型

若要添加新的应用程序类型,请在 $(VCTargetsPath)\Application Type\ 下创建 MyApplicationType 文件夹,并在其中创建 Defaults.props 文件。 应用程序类型至少需要一个修订,因此还要创建 $(VCTargetsPath)\Application Type\MyApplicationType\1.0 文件夹,并在其中创建 Defaults.props 文件。 还应创建 $(VCTargetsPath)\ApplicationType\MyApplicationType\1.0\Platforms 文件夹,并在其中创建至少一个平台。

$(ApplicationType)$(ApplicationTypeRevision) 属性在用户界面中不可见。 它们在项目模板中定义,在创建项目后无法更改。

.vcxproj 导入树

Microsoft C++ 属性和目标文件的简化导入树如下:

$(VCTargetsPath)\Microsoft.Cpp.Default.props
    $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props
    $(VCTargetsPath)\ImportBefore\Default\*.props
    $(VCTargetsPath)\应用类型\$(ApplicationType)\Default.props
    $(VCTargetsPath)\应用类型\$(ApplicationType)\$(ApplicationTypeRevision)\默认.props
    $(VCTargetsPath)\应用程序类型\$(ApplicationType)\$(ApplicationTypeRevision)\平台\$(Platform)\Platform.default.props
    $(VCTargetsPath)\ImportAfter\Default\*.props

Windows 桌面项目不定义 $(ApplicationType),因此它们仅导入

$(VCTargetsPath)\Microsoft.Cpp.Default.props
    $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props
    $(VCTargetsPath)\ImportBefore\Default\*.props
    $(VCTargetsPath)\Platforms\$(Platform)\Platform.default.props
    $(VCTargetsPath)\ImportAfter\Default\*.props

我们将使用 $(_PlatformFolder) 属性来保存 $(Platform) 平台文件夹位置。 此属性为

$(VCTargetsPath)\平台\$(Platform)

适用于 Windows 桌面应用,

$(VCTargetsPath)\应用程序类型\$(ApplicationType)\$(ApplicationTypeRevision)\平台\$(Platform)

适用于其他所有情况。

属性文件按以下顺序导入:

$(VCTargetsPath)\Microsoft.Cpp.props
    $(_PlatformFolder)\Platform.props
        $(VCTargetsPath)\Microsoft.Cpp.Platform.props
            $(_PlatformFolder)\ImportBefore\*.props
            $(_PlatformFolder)\PlatformToolsets\$(PlatformToolset)\Toolset.props
            $(_PlatformFolder)\ImportAfter\*.props

目标文件按以下顺序导入:

$(VCTargetsPath)\Microsoft.Cpp.targets
    $(VCTargetsPath)\Microsoft.Cpp.Current.targets
        $(_PlatformFolder)\Platform.targets
            $(VCTargetsPath)\Microsoft.Cpp.Platform.targets
                $(_PlatformFolder)\ImportBefore\*.targets
                $(_PlatformFolder)\PlatformToolsets\$(PlatformToolset)\Toolset.target
                $(_PlatformFolder)\ImportAfter\*.targets

如果需要为工具集定义一些默认属性,可以将文件添加到相应的 ImportBefore 和 ImportAfter 文件夹。

编写 Toolset.props 和 Toolset.targets 文件

Toolset.propsToolset.targets 文件对使用此工具集时生成过程中的情况有完全的控制。 它们还可以控制可用的调试器、某些 IDE 用户界面,例如 属性页 对话框中的内容,以及项目行为的其他一些方面。

尽管工具集可以覆盖整个生成过程,但通常你只想让工具集修改或添加一些生成步骤,或者将不同的生成工具用作现有生成过程的一部分。 为了实现这个目标,你的工具集可以导入许多常见的属性和目标文件。 根据工具集要执行的操作,这些文件可用于用作导入或示例:

  • $(VCTargetsPath)\Microsoft.CppCommon.targets

    此文件定义本机生成过程的主要部分,并导入:

    • $(VCTargetsPath)\Microsoft.CppBuild.targets

    • $(VCTargetsPath)\Microsoft.BuildSteps.targets

    • $(MSBuildToolsPath)\Microsoft.Common.Targets

  • $(VCTargetsPath)\Microsoft.Cpp.Common.props

    为使用 Microsoft 编译器和目标 Windows 的工具集设置默认值。

  • $(VCTargetsPath)\Microsoft.Cpp.WindowsSDK.props

    此文件确定 Windows SDK 位置,并为面向 Windows 的应用定义一些重要属性。

将特定于工具集的目标与默认C++生成过程集成

默认C++生成过程在 Microsoft.CppCommon.targets中定义。 那里的目标不会调用任何特定的生成工具;它们指定主要的生成步骤、顺序以及依赖关系。

C++生成有三个主要步骤,这些步骤由以下目标表示:

  • BuildGenerateSources

  • BuildCompile

  • BuildLink

由于每个生成步骤都可以独立执行,因此在一个步骤中运行的目标不能依赖于作为不同步骤一部分运行的目标中定义的项组和属性。 此划分允许实现某些构建性能优化。 尽管默认情况下不会使用它,但仍建议你遵循这种分离原则。

在每个步骤中运行的目标由以下属性控制:

  • $(BuildGenerateSourcesTargets)

  • $(BuildCompileTargets)

  • $(BeforeBuildLinkTargets)

每个步骤还具有 Before 和 After 属性。

<Target
  Name="_BuildGenerateSourcesAction"
  DependsOnTargets="$(CommonBuildOnlyTargets);$(BeforeBuildGenerateSourcesTargets);$(BuildGenerateSourcesTargets);$(AfterBuildGenerateSourcesTargets)" />

<Target
  Name="\_BuildCompileAction"
  DependsOnTargets="$(CommonBuildOnlyTargets);$(BeforeBuildCompileTargets);$(BuildCompileTargets);$(AfterBuildCompileTargets)" />

<Target
  Name="\_BuildLinkAction"
  DependsOnTargets="$(CommonBuildOnlyTargets);$(BeforeBuildLinkTargets);$(BuildLinkTargets);$(AfterBuildLinkTargets)" />

有关每个步骤中包含的目标示例,请参阅 Microsoft.CppBuild.targets 文件:

<BuildCompileTargets Condition="'$(ConfigurationType)'\!='Utility'">
  $(BuildCompileTargets);
  _ClCompile;
  _ResGen;
  _ResourceCompile;
  $(BuildLibTargets);
</BuildCompileTargets>

如果查看目标(如 _ClCompile),你将看到它们不直接执行任何操作,而是依赖于其他目标,包括 ClCompile

<Target Name="_ClCompile"
  DependsOnTargets="$(BeforeClCompileTargets);$(ComputeCompileInputsTargets);MakeDirsForCl;ClCompile;$(AfterClCompileTargets)" >
</Target>

ClCompile 和其他特定于生成工具的目标在 Microsoft.CppBuild.targets中定义为空目标:

<Target Name="ClCompile"/>

由于 ClCompile 目标为空,除非被工具集替代,否则不会执行任何实际的生成操作。 工具集目标可以替代 ClCompile 目标,也就是说,在导入 Microsoft.CppBuild.targets后,它们可以包含另一个 ClCompile 定义:

<Target Name="ClCompile"
  Condition="'@(ClCompile)' != ''"
  DependsOnTargets="SelectClCompile">
  <!-- call some MSBuild tasks -->
</Target>

尽管在 Visual Studio 实现跨平台支持之前创建了名称,但 ClCompile 目标不必调用 CL.exe。 它还可以使用适当的 MSBuild 任务调用 Clang、gcc 或其他编译器。

ClCompile 目标不应该有任何依赖项,除了 SelectClCompile 目标,因为它是使单文件编译命令在 IDE 中正常工作的必要条件。

要在工具集目标中使用的 MSBuild 任务

若要调用实际的生成工具,目标需要调用 MSBuild 任务。 有一个基本的 Exec 任务,可用于指定要运行的命令行。 但是,生成工具通常有许多选项、输入和输出用于跟踪增量生成,因此,为它们执行特殊任务更有意义。 例如,CL 任务将 MSBuild 属性转换为 CL.exe 开关,将其写入响应文件,并调用 CL.exe。 它还跟踪所有输入和输出文件,以便以后进行增量生成。 有关详细信息,请参阅增量生成和最新检查

Microsoft.Cpp.Common.Tasks.dll 模块执行以下任务:

  • BSCMake

  • CL

  • ClangCompile(clang-gcc 开关)

  • LIB

  • LINK

  • MIDL

  • Mt

  • RC

  • XDCMake

  • CustomBuild(如 Exec,但具有输入和输出跟踪)

  • SetEnv

  • GetOutOfDateItems

如果你有一个执行与现有工具相同的操作的工具,并且具有类似的命令行开关(如 clang-cl 和 CL),则可以对两者使用相同的任务。

如果需要为生成工具创建新任务,可以从以下选项中进行选择:

  1. 如果你很少使用此任务,或者多花几秒钟对你的生成无关紧要,则可以使用 MSBuild“内联”任务:

    • Xaml 任务(自定义生成规则)

      有关 Xaml 任务声明的一个示例,请参阅 $(VCTargetsPath)\BuildCustomizations\masm.xml,有关其用法,请参阅 $(VCTargetsPath)\BuildCustomizations\masm.targets

    • 代码任务

  2. 如果想要更好的任务性能或只需要更复杂的功能,请使用常规 MSBuild 任务编写 过程。

    如果工具命令行上未列出该工具的所有输入和输出,如在 CLMIDLRC 事例中一样,如果希望自动输入和输出文件跟踪和 .tlog 文件创建,请从 Microsoft.Build.CPPTasks.TrackedVCToolTask 类派生任务。 目前,尽管基本 ToolTask 类有文档,但对于 TrackedVCToolTask 类的详细信息,缺乏示例和文档。 如果您对此特别感兴趣,请在 开发人员社区中提出请求。

增量生成和最新检查

默认 MSBuild 增量生成目标使用 InputsOutputs 属性。 如果指定它们,则 MSBuild 仅当任何输入的时间戳都高于所有输出时,才会调用目标。 由于源文件通常包括或导入其他文件,并且生成工具会根据工具选项生成不同的输出,因此很难在 MSBuild 目标中指定所有可能的输入和输出。

为了管理此问题,C++生成使用不同的技术来支持增量生成。 大多数目标不会指定输入和输出,因此,始终在生成期间运行。 目标调用的任务会将有关所有输入和输出的信息写入 tlog 扩展名为 .tlog 的文件。 .tlog 文件供后续构建使用,以检查哪些内容已经更改并需要重新生成,以及 up-to-date 是什么。 .tlog 文件也是 IDE 中默认的生成最新检查的唯一源。

若要确定所有输入和输出,本机工具任务使用 tracker.exe 和 MSBuild 提供的 FileTracker 类。

Microsoft.Build.CPPTasks.Common.dll 定义公共抽象基类 TrackedVCToolTask。 大多数原生工具任务都派生自此类。

从 Visual Studio 2017 update 15.8 开始,可以使用在 Microsoft.Cpp.Common.Tasks.dll 中实现的 GetOutOfDateItems 任务为具有已知输入和输出的自定义目标生成 .tlog 文件。 或者,可以使用 WriteLinesToFile 任务创建它们。 以 $(VCTargetsPath)\BuildCustomizations\masm.targets 中的 _WriteMasmTlogs 目标为例。

.tlog 文件

有三种类型的 .tlog 文件:读取写入命令行。 读取和写入 .tlog 文件用于增量生成以及 IDE 中的最新检查。 命令行 .tlog 文件仅用于增量生成。

MSBuild 提供以下帮助程序类来读取和写入 .tlog 文件:

FlatTrackingData 类可用于访问读取和写入 .tlog 文件,并识别比输出更新的输入或输出是否缺失。 它在最新检查中使用。

命令行 .tlog 文件包含有关生成中使用的命令行的信息。 它们仅用于增量生成,而不是 up-to日期检查,因此内部格式由生成它们的 MSBuild 任务确定。

读取 .tlog 格式

读取 .tlog 文件 (*.read.*.tlog) 包含有关源文件及其依赖项的信息

行首的插入点 (^) 表示一个或多个源。 共享相同依赖项的源由垂直条分隔(|)。

依赖项文件在源之后列出,每个文件在其自己的行中列出。 所有文件名都是完整路径。

例如,假设项目源位于 F:\test\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1。 如果源文件(Class1.cpp)包含以下内容:

#include "stdafx.h" //precompiled header
#include "Class1.h"

然后,CL.read.1.tlog 文件包含源文件,后跟其两个依赖项:

^F:\TEST\CONSOLEAPPLICATION1\CONSOLEAPPLICATION1\CLASS1.CPP
F:\TEST\CONSOLEAPPLICATION1\CONSOLEAPPLICATION1\DEBUG\CONSOLEAPPLICATION1.PCH
F:\TEST\CONSOLEAPPLICATION1\CONSOLEAPPLICATION1\CLASS1.H

不需要以大写形式编写文件名,但对于某些工具来说,这是一种方便。

写入 .tlog 格式

写入 .tlog (*.write.*.tlog) 文件连接源和输出。

行首的插入点 (^) 表示一个或多个源。 多个源由垂直条分隔(|)。

从源生成的输出文件应列在源之后,每个文件在其自己的行中列出。 所有文件名都必须是完整路径。

例如,对于具有附加源文件 Class1.cpp的简单 ConsoleApplication 项目,link.write.1.tlog 文件可能包含:

^F:\TEST\CONSOLEAPPLICATION1\CONSOLEAPPLICATION1\DEBUG\CLASS1.OBJ|F:\TEST\CONSOLEAPPLICATION1\CONSOLEAPPLICATION1\DEBUG\CONSOLEAPPLICATION1.OBJ|F:\TEST\CONSOLEAPPLICATION1\CONSOLEAPPLICATION1\DEBUG\STDAFX.OBJ
F:\TEST\CONSOLEAPPLICATION1\DEBUG\CONSOLEAPPLICATION1.ILK
F:\TEST\CONSOLEAPPLICATION1\DEBUG\CONSOLEAPPLICATION1.EXE
F:\TEST\CONSOLEAPPLICATION1\DEBUG\CONSOLEAPPLICATION1.PDB

设计时生成

在 IDE 中,.vcxproj项目使用一组 MSBuild 目标从项目获取其他信息并重新生成输出文件。 其中一些目标仅用于设计时构建,但其中许多目标用于常规构建和设计时构建。

有关设计时生成的常规信息,请参阅针对设计时生成的 CPS 文档。 本文档仅部分适用于 Visual C++ 项目。

设计时生成文档中提及的 CompileDesignTimeCompile 目标永远不会为 .vcxproj 项目运行。 Visual C++ .vcxproj 项目使用不同的设计时目标来获取 IntelliSense 信息。

IntelliSense 信息的设计时目标

.vcxproj项目中使用的设计时目标在 $(VCTargetsPath)\Microsoft.Cpp.DesignTime.targets中定义。

GetClCommandLines 目标收集 IntelliSense 的编译器选项:

<Target
  Name="GetClCommandLines"
  Returns="@(ClCommandLines)"
  DependsOnTargets="$(DesignTimeBuildInitTargets);$(ComputeCompileInputsTargets)">
  • DesignTimeBuildInitTargets - 仅用于设计时的目标,是设计时生成初始化所必需的。 除此之外,这些目标还禁用某些常规生成功能以提高性能。

  • ComputeCompileInputsTargets – 一组修改编译器选项和项的目标。 这些目标在设计时和常规生成中运行。

目标调用 CLCommandLine 任务来创建用于 IntelliSense 的命令行。 同样,尽管名称如此,但它不仅可以处理 CL 选项,还可以处理 Clang 和 gcc 选项。 编译器开关的类型由 ClangMode 属性控制。

目前,由 CLCommandLine 任务生成的命令行始终使用 CL 开关(即使在 Clang 模式下),因为它们更易于 IntelliSense 引擎进行分析。

如果要添加在编译之前运行的目标,无论是常规构建还是设计时构建,请确保它不会中断设计时构建或影响性能。 测试目标的最简单方法是打开开发人员命令提示符并运行以下命令:

msbuild /p:SolutionDir=*solution-directory-with-trailing-backslash*;Configuration=Debug;Platform=Win32;BuildingInsideVisualStudio=true;DesignTimebuild=true /t:\_PerfIntellisenseInfo /v:d /fl /fileloggerparameters:PerformanceSummary \*.vcxproj

此命令会生成一个详细的生成日志,msbuild.log,该日志具有最终目标和任务的性能摘要。

确保在所有操作中使用 Condition ="'$(DesignTimeBuild)' != 'true'",这些操作仅对常规生成有意义,而不适用于设计时生成。

生成源的设计时目标

对于桌面本机项目,此功能默认处于禁用状态,并且当前在缓存项目上不受支持。

如果为项目项定义了 GeneratorTarget 元数据,则当加载项目和更改源文件时,目标会自动运行。

例如,若要从 .xaml 文件自动生成.cpp或 .h 文件,$(VSInstallDir)\MSBuild\Microsoft\WindowsXaml\v16.0\*\Microsoft.Windows.UI.Xaml.CPP.Targets 文件定义以下实体:

<ItemDefinitionGroup>
  <Page>
    <GeneratorTarget>DesignTimeMarkupCompilation</GeneratorTarget>
  </Page>
  <ApplicationDefinition>
    <GeneratorTarget>DesignTimeMarkupCompilation</GeneratorTarget>
  </ApplicationDefinition>
</ItemDefinitionGroup>
<Target Name="DesignTimeMarkupCompilation">
  <!-- BuildingProject is used in Managed builds (always true in Native) -->
  <!-- DesignTimeBuild is used in Native builds (always false in Managed) -->
  <CallTarget Condition="'$(BuildingProject)' != 'true' Or $(DesignTimeBuild) == 'true'" Targets="DesignTimeMarkupCompilationCT" />
</Target>

若要使用 Task.HostObject 获取源文件的未保存内容,应将目标和任务注册为 pkgdef 中给定项目的 MsbuildHostObjects

\[$RootKey$\\Projects\\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\\MSBuildHostObjects\]
\[$RootKey$\\Projects\\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\\MSBuildHostObjects\\DesignTimeMarkupCompilationCT;CompileXaml\]
@="{83046B3F-8984-444B-A5D2-8029DEE2DB70}"

Visual Studio IDE 中的 Visual C++项目扩展性

Visual C++ 项目系统基于 VS Project System,并使用其扩展点。 但是,项目层次结构实现特定于 Visual C++,而不是基于 CPS,因此层次结构扩展性仅限于项目项。

项目属性页面

有关常规设计信息,请参阅 VC++ 项目的框架多目标设定

简单来说,C++项目的 项目属性 对话框中看到的属性页由 规则 文件定义。 规则文件指定要在属性页上显示的属性集,以及它们应保存在项目文件中的方式和位置。 规则文件是指使用 Xaml 格式的 .xml 文件。 用于序列化它们的类型在 Microsoft.Build.Framework.XamlTypes中介绍。 有关项目中规则文件使用的详细信息,请参阅 属性页 XML 规则文件

必须将规则文件添加到 PropertyPageSchema 项组:

<ItemGroup>
  <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\general.xml;"/>
  <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\general_file.xml">
    <Context>File</Context>
  </PropertyPageSchema>
</ItemGroup>

Context 元数据限制规则可见性,该可见性也受规则类型控制,并且可以具有以下值之一:

Project | File | PropertySheet

CPS 支持上下文类型的其他值,但在 Visual C++ 项目中不使用它们。

如果规则应在多个上下文中可见,请使用分号(;) 分隔上下文值,如下所示:

<PropertyPageSchema Include="$(MyFolder)\MyRule.xml">
  <Context>Project;PropertySheet</Context>
</PropertyPageSchema>

规则格式和主要类型

规则格式非常简单,因此本部分仅描述影响规则在用户界面中外观的属性。

<Rule
  Name="ConfigurationGeneral"
  DisplayName="General"
  PageTemplate="generic"
  Description="General"
  xmlns="http://schemas.microsoft.com/build/2009/properties">

PageTemplate 属性定义规则在 属性页 对话框中的显示方式。 该属性可以具有以下值之一:

属性 描述
generic 所有属性显示在类别标题下的一页上
规则在 ProjectPropertySheet 上下文中可见,但在 File中不可见。

示例:$(VCTargetsPath)\1033\general.xml
tool 类别显示为子页。
规则可在所有上下文中可见:ProjectPropertySheetFile
只有在项目具有 Rule.DataSource中定义的 ItemType 的项目时,该规则才会显示在项目属性中,除非规则名称包含在 ProjectTools 项组中。

示例:$(VCTargetsPath)\1033\clang.xml
debugger 该页显示为调试页的一部分。
类别正在被忽略。
规则名称应与调试启动器 MEF 对象的 ExportDebugger 属性匹配。

示例:$(VCTargetsPath)\1033\debugger_local_windows.xml
custom 自定义模板。 模板的名称应与 PropertyPageUIFactoryProvider MEF 对象的 ExportPropertyPageUIFactoryProvider 属性匹配。 请参阅 Microsoft.VisualStudio.ProjectSystem.Designers.Properties.IPropertyPageUIFactoryProvider

示例:$(VCTargetsPath)\1033\userMacros.xml

如果规则使用基于属性网格的模板之一,则它可以对其属性使用这些扩展点:

扩展规则

如果要使用现有规则,但需要添加或删除(即隐藏)仅几个属性,则可以创建 扩展规则

替代规则

也许你希望工具集使用大部分项目默认规则,但只替换其中一个或几个规则。 例如,假设只想更改 C/C++ 规则以显示不同的编译器开关。 你可以提供与现有规则相同的名称和显示名称的新规则,并在导入默认 cpp 目标后将其包含在 PropertyPageSchema 项组中。 在项目中,具有给定名称的规则只会使用一个,并且包含在 PropertyPageSchema 项组中的最后一个规则会生效。

项目内容

ProjectItemsSchema.xml 文件定义被视为项目项的项的 ContentTypeItemType 值,并定义 FileExtension 元素以确定新文件被添加到哪个项组。

默认 ProjectItemsSchema 文件位于 $(VCTargetsPath)\1033\ProjectItemsSchema.xml中。 若要对其进行扩展,必须创建具有新名称的架构文件,例如 MyProjectItemsSchema.xml

<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties">

  <ItemType Name="MyItemType" DisplayName="C/C++ compiler"/>

  <ContentType
    Name="MyItems"
    DisplayName="My items"
    ItemType=" MyItemType ">
  </ContentType>

  <FileExtension Name=".abc" ContentType=" MyItems"/>

</ProjectSchemaDefinitions>

然后在目标文件中,添加:

<ItemGroup>
  <PropertyPageSchema Include="MyProjectItemsSchema.xml"/>
</ItemGroup>

示例:$(VCTargetsPath)\BuildCustomizations\masm.xml

调试器

Visual Studio 中的调试服务支持调试引擎的扩展性。 有关详细信息,请参阅以下示例:

若要为调试会话指定调试引擎和其他属性,必须实现 调试启动器 MEF 组件,并添加 debugger 规则。 有关示例,请参阅 $(VCTargetsPath)\1033\debugger_local_windows.xml 文件。

部署

.vcxproj 项目将 Visual Studio 项目系统扩展性用于部署提供程序

生成最新检查

默认情况下,生成最新检查要求在生成期间为所有生成输入和输出在 $(TlogLocation) 文件夹中创建读取 .tlog 和写入 .tlog文件。

若要使用自定义最新检查,请执行以下操作:

  1. 通过在 Toolset.targets 文件中添加 NoVCDefaultBuildUpToDateCheckProvider 功能来禁用默认 up-to日期检查:

    <ItemGroup>
      <ProjectCapability Include="NoVCDefaultBuildUpToDateCheckProvider" />
    </ItemGroup>
    
  2. 实现自己的 IBuildUpToDateCheckProvider

项目升级

默认.vcxproj项目升级程序

默认.vcxproj项目升级程序更改 PlatformToolsetApplicationTypeRevision、MSBuild 工具集版本和 .NET Framework。 最后两个始终更改为 Visual Studio 版本默认值,但 PlatformToolsetApplicationTypeRevision 可以由特殊的 MSBuild 属性控制。

升级程序使用以下条件来确定是否可以升级项目:

  1. 对于定义 ApplicationTypeApplicationTypeRevision的项目,有一个具有比当前修订号更高的文件夹。

  2. 为当前工具集定义属性 _UpgradePlatformToolsetFor_<safe_toolset_name>,其值不等于当前工具集。

    在这些属性名称中,<safe_toolset_name> 表示工具集名称,其中所有非字母数字字符都替换为下划线(_)。

当项目可以升级时,它会参与解决方案重定目标。 有关详细信息,请参阅 IVsTrackProjectRetargeting2

如果你希望在项目使用特定工具集时在解决方案资源管理器中修饰项目名称,请定义 _PlatformToolsetShortNameFor_<safe_toolset_name> 属性。

有关 _UpgradePlatformToolsetFor_<safe_toolset_name>_PlatformToolsetShortNameFor_<safe_toolset_name> 属性定义的示例,请参阅 Microsoft.Cpp.Default.props 文件。 有关用法示例,请参阅 $(VCTargetPath)\Microsoft.Cpp.Platform.targets 文件。

自定义项目升级程序

若要使用自定义项目升级程序对象,请实现 MEF 组件,如下所示:

/// </summary>
[Export("MyProjectUpgrader", typeof(IProjectRetargetHandler))]
[Export(typeof(IProjectRetargetHandler))]
[ExportMetadata("Name", "MyProjectUpgrader")]
[OrderPrecedence(20)]
[PartMetadata(ProjectCapabilities.Requires, ProjectCapabilities.VisualC)]

internal class MyProjectUpgrader: IProjectRetargetHandler
{
    // ...
}

代码可以导入和调用默认.vcxproj升级程序对象:

// ...
[Import("VCDefaultProjectUpgrader")]
// ...
    IProjectRetargetHandler Lazy<IProjectRetargetHandler>
    VCDefaultProjectUpgrader { get; set; }
// ...

IProjectRetargetHandlerMicrosoft.VisualStudio.ProjectSystem.VS.dll 中定义,类似于 IVsRetargetProjectAsync

定义 VCProjectUpgraderObjectName 属性,告知项目系统使用自定义升级程序对象:

<PropertyGroup>
  <VCProjectUpgraderObjectName>MyProjectUpgrader</VCProjectUpgraderObjectName>
</PropertyGroup>

禁用项目升级

若要禁用项目升级,请使用 NoUpgrade 值:

<PropertyGroup>
  <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>
</PropertyGroup>

项目缓存和扩展性

为了在 Visual Studio 2017 中使用大型C++解决方案时提高性能,引入了 项目缓存。 它作为填充项目数据的 SQLite 数据库实现,然后用于加载项目,而无需将 MSBuild 或 CPS 项目加载到内存中。

由于缓存中加载的.vcxproj项目不存在 CPS 对象,因此无法创建导入 UnconfiguredProjectConfiguredProject 的扩展的 MEF 组件。 为了支持扩展性,Visual Studio 检测到项目是使用(还是可能使用)MEF 扩展时,不会使用项目缓存。

这些项目类型始终完全加载,并且内存中具有 CPS 对象,因此会为其创建所有 MEF 扩展:

  • 创业项目

  • 具有自定义项目升级程序的项目,即定义了 VCProjectUpgraderObjectName 属性的项目

  • 并非以桌面版 Windows 为目标的项目,也就是那些定义了 ApplicationType 属性的项目

  • 共享项项目 (.vcxitems) 以及任何通过导入 .vcxitems 项目来引用它们的项目。

如果未检测到这些条件,则会创建项目缓存。 缓存包括 MSBuild 项目中的所有数据,这些数据都需要在 VCProjectEngine 接口上回答 get 查询。 这意味着,扩展完成的 MSBuild 属性和目标文件级别的所有修改应仅适用于从缓存加载的项目。

发布扩展

有关如何创建 VSIX 文件的信息,请参阅 发布 Visual Studio 扩展。 有关如何将文件添加到特殊安装位置的信息,例如,若要在 $(VCTargetsPath)下添加文件,请参阅 在扩展文件夹外部安装。

其他资源

Microsoft生成系统(MSBuild)为项目文件提供生成引擎和基于 XML 的可扩展格式。 你应该熟悉 MSBuild 的基本 概念,以及 用于 Visual C++ 的 MSBuild 的工作原理,以便扩展 Visual C++ 项目系统。

托管扩展性框架(MEF)提供 CPS 和 Visual C++ 项目系统使用的扩展 API。 有关 CPS 如何使用 MEF 的概述,请参阅 MEF 的 VSProjectSystem 概述中的 CPS 和 MEF

可以自定义现有的生成系统,以添加生成步骤或新的文件类型。 有关更多信息,请参阅 MSBuild (Visual C++) 概述使用项目属性