Visual Studio C++ 项目系统可扩展性和工具集集成

Visual C++ 项目系统用于 .vcxproj 文件。 它基于 Visual Studio 通用项目系统 (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 Store、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)\
            PlatformToolsets\
                $(PlatformToolset)

对于 Windows 桌面项目,当 $(VCTargetsPath) 为空时,使用 $(ApplicationType)\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) 的“平台”文件夹下的所有文件夹名称都作为项目的可用平台选项显示在 IDE 中。

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

添加新的应用程序类型

若要添加新的应用程序类型,请在 $(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)\Application Type\$(ApplicationType)\Default.props
    $(VCTargetsPath)\Application Type\$(ApplicationType)\$(ApplicationTypeRevision)\Default.props
    $(VCTargetsPath)\Application Type\$(ApplicationType)\$(ApplicationTypeRevision)\Platforms\$(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 文件检查已更改和需要重新生成的内容,以及最新的内容。 .tlog 文件也是 IDE 中默认的生成最新检查的唯一源。

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

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

从 Visual Studio 2017 更新版 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 文件包含有关生成中使用的命令行的信息。 它们仅用于增量生成,而不是最新检查,因此内部格式由生成它们的 MSBuild 任务确定。

读取 .tlog 格式

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

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

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

例如,假设项目源位于 F:\test\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 项目系统,并使用其扩展点。 但是,项目层次结构的实现特定于 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
除非规则名称包含在 ItemType 项组中,否则只有当项目具有包含 Rule.DataSource 中定义的 ProjectTools 的项时,规则才在项目属性中可见。

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

示例: $(VCTargetsPath)\1033\debugger_local_windows.xml
custom 自定义模板。 模板的名称应与 ExportPropertyPageUIFactoryProvider MEF 对象的 PropertyPageUIFactoryProvider 属性匹配。 请参阅 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 功能来禁用默认的最新检查:

    <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 项目引用它们的任何项目。

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

寄送扩展

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

其他资源

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

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

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