Partilhar via


Extensibilidade do sistema de projeto do Visual Studio C++ e integração do conjunto de ferramentas

O sistema de projeto do Visual C++ é usado em arquivos .vcxproj. Ele é baseado no Common Project System (CPS) do Visual Studio e fornece pontos de extensibilidade específicos e adicionais do C++ para facilitar a integração de novos conjuntos de ferramentas, arquiteturas de build e plataformas de destino.

Estrutura de destinos do MSBuild em C++

Todos os arquivos .vcxproj importam estes arquivos:

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

Esses arquivos definem pouco por si mesmos. Em vez disso, eles importam outros arquivos com base nestes valores da propriedade:

  • $(ApplicationType)

    Exemplos: Windows Store, Android, Linux

  • $(ApplicationTypeRevision)

    Essa deve ser uma sequência de versão válida, do formato major.minor[.build[.revision]].

    Exemplos: 1.0, 10.0.0.0

  • $(Platform)

    A arquitetura de build, denominada "Platform" por razões de histórico.

    Exemplos: Win32, x86, x64, ARM

  • $(PlatformToolset)

    Exemplos: v140, v141, v141_xp, llvm

Estes valores da propriedade especificam nomes de pasta na pasta raiz $(VCTargetsPath):

$(VCTargetsPath)\
    Tipo de Aplicativo\
        $(ApplicationType)\
            $(ApplicationTypeRevision)\
                Plataformas\
                    $(Platform)\
                        Conjuntos de ferramentas da plataforma\
                            $(PlatformToolset)
    Plataformas\
        $(Platform)\
            Conjuntos de ferramentas da plataforma\
                $(PlatformToolset)

A pasta $(VCTargetsPath)\Platforms\ é usada quando $(ApplicationType) está vazia, para projetos da área de trabalho do Windows.

Adicionar um novo conjunto de ferramentas da plataforma

Para adicionar um novo conjunto de ferramentas, por exemplo, "MyToolset" na plataforma existente do Win32, crie uma pasta MyToolset em $(VCTargetsPath)\Platforms\Win32\PlatformToolsets\ e crie os arquivos Toolset.props e Toolset.targets nela.

Cada nome de pasta em PlatformToolsets aparece na caixa de diálogo Propriedades do projeto como um Conjunto de Ferramentas da Plataforma disponível para a plataforma especificada, conforme mostrado aqui:

A propriedade Conjunto de Ferramentas da Plataforma na caixa de diálogo Páginas de Propriedades do projeto

Crie pastas MyToolset semelhantes, bem como arquivos Toolset.props e Toolset.targets em cada pasta de plataforma existente com suporte desse conjunto de ferramentas.

Adicionar uma nova plataforma

Para adicionar uma nova plataforma, por exemplo, "MyPlatform", crie uma pasta MyPlatform em $(VCTargetsPath)\Platforms\ e os arquivos Platform.default.props, Platform.props e Platform.targets nela. Crie também uma pasta $(VCTargetsPath)\Platforms\MyPlatform\PlatformToolsets\ e, pelo menos, um conjunto de ferramentas nela.

Todos os nomes de pasta em Platforms de cada $(ApplicationType) e $(ApplicationTypeRevision) aparecem no IDE como opções de Plataforma disponíveis para um projeto.

A opção Nova plataforma na caixa de diálogo Nova Plataforma de Projeto

Adicionar um novo tipo de aplicativo

Para adicionar um novo tipo de aplicativo, crie uma pasta MyApplicationType em $(VCTargetsPath)\Application Type\ e um arquivo Defaults.props nela. Pelo menos uma revisão é necessária para um tipo de aplicativo, portanto, crie também uma pasta $(VCTargetsPath)\Application Type\MyApplicationType\1.0 e um arquivo Defaults.props nela. Você também deve criar uma pasta $(VCTargetsPath)\ApplicationType\MyApplicationType\1.0\Platforms e, pelo menos, uma plataforma nela.

As propriedades $(ApplicationType) e $(ApplicationTypeRevision) não são visíveis na interface do usuário. Elas são definidas nos modelos do projeto e não podem ser alteradas após a criação do projeto.

A árvore de importação .vcxproj

Uma árvore simplificada de importações para arquivos de objetos e destinos do Microsoft C++ tem a seguinte aparência:

$(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

Os projetos da Área de Trabalho do Windows não definem $(ApplicationType), então eles importam apenas

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

Usaremos a propriedade $(_PlatformFolder) para reter as localizações das pastas da plataforma $(Platform). Essa propriedade é

$(VCTargetsPath)\Plataformas\$(Platform)

para aplicativos da Área de Trabalho do Windows

$(VCTargetsPath)\Application Type\$(ApplicationType)\$(ApplicationTypeRevision)\Platforms\$(Platform)

para todo o resto.

Os arquivos de objetos são importados nesta ordem:

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

Os arquivos de destino são importados nesta ordem:

$(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

Se precisar definir algumas propriedades padrão para o conjunto de ferramentas, é possível adicionar arquivos às pastas ImportBefore e ImportAfter apropriadas.

Criar arquivos Toolset.props e Toolset.targets

Os arquivos Toolset.props e Toolset.targets têm controle total sobre o que acontece durante um build quando esse conjunto de ferramentas é usado. Eles também podem controlar os depuradores disponíveis, parte da interface do usuário do IDE, como o conteúdo na caixa de diálogo Páginas de Propriedades e alguns outros aspectos do comportamento do projeto.

Embora um conjunto de ferramentas possa substituir todo o processo de build, geralmente convém apenas que o conjunto de ferramentas modifique ou adicione algumas etapas de build, ou use ferramentas de build diferentes, como parte de um processo de build existente. Para atingir esse objetivo, há uma série de arquivos comuns de objetos e destinos que o conjunto de ferramentas pode importar. Dependendo do que você deseja que o conjunto de ferramentas faça, estes arquivos podem ser úteis para usar como importações ou exemplos:

  • $(VCTargetsPath)\Microsoft.CppCommon.targets

    Este arquivo define as partes principais do processo de build nativo e importa:

    • $(VCTargetsPath)\Microsoft.CppBuild.targets

    • $(VCTargetsPath)\Microsoft.BuildSteps.targets

    • $(MSBuildToolsPath)\Microsoft.Common.Targets

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

    Define padrões para conjuntos de ferramentas que usam os compiladores da Microsoft e têm o Windows como destino.

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

    Esse arquivo determina a localização do SDK do Windows e define algumas propriedades importantes para aplicativos direcionados ao Windows.

Integrar destinos específicos do conjunto de ferramentas com o processo de build padrão em C++

O processo de build padrão em C++ é definido em Microsoft.CppCommon.targets. Lá, os destinos não chamam nenhuma ferramenta de build específica; eles especificam as principais etapas de build, sua ordem e dependências.

O build em C++ tem três etapas principais, que são representadas pelos seguintes destinos:

  • BuildGenerateSources

  • BuildCompile

  • BuildLink

Como cada etapa de build pode ser executada de forma independente, os destinos executados em uma etapa não podem depender dos grupos de itens e das propriedades definidas nos destinos que são executados como parte de uma etapa diferente. Essa divisão permite certas otimizações de desempenho do build. Embora não seja usada por padrão, incentivamos você a usar essa separação.

Os destinos executados dentro de cada etapa são controlados por estas propriedades:

  • $(BuildGenerateSourcesTargets)

  • $(BuildCompileTargets)

  • $(BeforeBuildLinkTargets)

Cada etapa também tem propriedades Before e 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)" />

Consulte o arquivo Microsoft.CppBuild.targets para obter exemplos dos destinos incluídos em cada etapa:

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

Se você observar os destinos, como _ClCompile, perceberá que eles não fazem nada diretamente sozinhos, mas dependem de outros destinos, incluindo ClCompile:

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

O ClCompile e outros destinos específicos da ferramenta de build são definidos como destinos vazios em Microsoft.CppBuild.targets:

<Target Name="ClCompile"/>

Como o destino ClCompile está vazio, a menos que seja substituído por um conjunto de ferramentas, nenhuma ação de build real é executada. Os destinos do conjunto de ferramentas podem substituir o destino ClCompile, ou seja, podem conter outra definição de ClCompile após a importação de Microsoft.CppBuild.targets:

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

Apesar do nome, que foi criado antes de o Visual Studio implementar o suporte multiplataforma, o destino ClCompile não precisa chamar CL.exe. Ele também pode chamar Clang, gcc ou outros compiladores usando tarefas apropriadas do MSBuild.

O destino ClCompile não deve ter dependências, exceto o destino SelectClCompile, necessário para que o comando de compilação de arquivo único funcione no IDE.

Tarefas do MSBuild a serem usadas em destinos do conjunto de ferramentas

Para invocar uma ferramenta de build real, o destino precisa chamar uma tarefa do MSBuild. Há uma tarefa Exec básica que permite especificar uma linha de comando a ser executada. No entanto, as ferramentas de build costumam ter muitas opções, entradas e saídas para rastrear build incrementais, portanto, faz mais sentido ter tarefas especiais para elas. Por exemplo, a tarefa CL converte propriedades do MSBuild em opções de CL.exe, faz a gravação delas em um arquivo de resposta e chama CL.exe. Ela também acompanha todos os arquivos de entrada e saída para builds incrementais posteriores. Para obter mais informações, confira Incremental builds and up-to-date checks.

O Microsoft.Cpp.Common.Tasks.dll implementa estas tarefas:

  • BSCMake

  • CL

  • ClangCompile (opções de clang-gcc)

  • LIB

  • LINK

  • MIDL

  • Mt

  • RC

  • XDCMake

  • CustomBuild (como Exec, mas com acompanhamento de entrada e saída)

  • SetEnv

  • GetOutOfDateItems

Caso tenha uma ferramenta que execute a mesma ação que uma ferramenta existente e apresente opções de linha de comando semelhantes (como clang-cl e CL), você pode usar a mesma tarefa para ambas.

Caso precise criar uma tarefa para uma ferramenta de build, você pode escolher entre as seguintes opções:

  1. Caso use essa tarefa raramente, ou caso alguns segundos não importem para o build, você pode usar tarefas "embutidas" do MSBuild:

    • Tarefa Xaml (uma regra de build personalizada)

      Para obter um exemplo de uma declaração de tarefa Xaml, confira $(VCTargetsPath)\BuildCustomizations\masm.xml e, para seu uso, confira $(VCTargetsPath)\BuildCustomizations\masm.targets.

    • Tarefa de código

  2. Se quiser um melhor desempenho da tarefa ou apenas precisar de uma funcionalidade mais complexa, use o processo regular de gravação de tarefas do MSBuild.

    Se nem todas as entradas e saídas da ferramenta estiverem listadas na linha de comando da ferramenta, como nos casos CL, MIDL e RC, e se quiser o acompanhamento automático de arquivos de entrada e saída e a criação de arquivos .tlog, derive sua tarefa da classe Microsoft.Build.CPPTasks.TrackedVCToolTask. No momento, embora haja documentação para a classe de base ToolTask, não há exemplos ou documentação para os detalhes da classe TrackedVCToolTask. Se isso for de particular interesse, adicione seu Serviço de Voz a uma solicitação na Developer Community.

Builds incrementais e verificações atualizadas

Os destinos de build incremental padrão do MSBuild usam os atributos Inputs e Outputs. Se você especificá-los, o MSBuild chamará o destino somente se qualquer uma das entradas tiver um carimbo de data/hora mais recente do que todas as saídas. Como os arquivos de origem costumam incluir ou importar outros arquivos e as ferramentas de build produzem saídas diferentes, dependendo das opções da ferramenta, é difícil especificar todas as entradas e saídas possíveis nos destinos do MSBuild.

Para gerenciar esse problema, o build em C++ usa uma técnica diferente para oferecer suporte a builds incrementais. A maioria dos destinos não especifica entradas e saídas e, como resultado, sempre é executada durante o build. As tarefas chamadas pelos destinos gravam informações sobre todas as entradas e saídas em arquivos tlog que têm uma extensão .tlog. Os arquivos .tlog são usados por builds posteriores para verificar o que mudou e precisa ser recompilado e o que está atualizado. Os arquivos .tlog também são a única fonte para a verificação atualizada de build padrão no IDE.

Para determinar todas as entradas e saídas, as tarefas de ferramentas nativas usam tracker.exe e a classe FileTracker fornecida pelo MSBuild.

O Microsoft.Build.CPPTasks.Common.dll define a classe base abstrata pública TrackedVCToolTask. A maioria das tarefas da ferramenta nativa é derivada dessa classe.

A partir da atualização 15.8 do Visual Studio 2017, você pode usar a tarefa GetOutOfDateItems implementada no Microsoft.Cpp.Common.Tasks.dll para produzir arquivos .tlog de destinos personalizados com entradas e saídas conhecidas. Como alternativa, você pode criá-los usando a tarefa WriteLinesToFile. Confira o destino _WriteMasmTlogs em $(VCTargetsPath)\BuildCustomizations\masm.targets como um exemplo.

arquivos .tlog

Há três tipos de arquivos .tlog: leitura, gravação e linha de comando. Os arquivos .tlog de leitura e gravação são usados por builds incrementais e pela verificação atualizada no IDE. Os arquivos .tlog de linha de comando são usados apenas em builds incrementais.

O MSBuild fornece estas classes auxiliares para arquivos .tlog de leitura e gravação:

A classe FlatTrackingData pode ser usada para acessar arquivos .tlog de leitura e gravação e identificar entradas mais recentes que as saídas ou se uma saída estiver ausente. Ela é usada na verificação atualizada.

Os arquivos .tlog de linha de comando contêm informações sobre as linhas de comando usadas no build. Eles são usados apenas em builds incrementais e não em verificações atualizadas, portanto, o formato interno é determinado pela tarefa do MSBuild que os produz.

Formato .tlog de leitura

Os arquivos .tlog de leitura (*.read.*.tlog) contêm informações sobre os arquivos de origem e suas dependências.

Um sinal de interpolação (^) no início de uma linha indica um ou mais códigos-fonte. Os códigos-fonte que compartilham as mesmas dependências são separados por uma barra vertical (|).

Os arquivos de dependência são listados após os códigos-fonte, cada um em sua própria linha. Todos os nomes de arquivo são caminhos completos.

Por exemplo, suponha que os códigos-fonte do projeto sejam encontrados em F:\test\ConsoleApplication1\ConsoleApplication1. Se o arquivo de origem, Class1.cpp, tiver essas inclusões,

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

então o arquivo CL.read.1.tlog contém o arquivo de origem seguido por suas duas dependências:

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

Não é necessário gravar nomes de arquivo em maiúsculas, mas é conveniente em algumas ferramentas.

Formato .tlog de gravação

Os arquivos .tlog (*.write.*.tlog) de gravação conectam códigos-fonte e saídas.

Um sinal de interpolação (^) no início de uma linha indica um ou mais códigos-fonte. Várias fontes são separadas por uma barra vertical (|).

Os arquivos de saída criados com os códigos-fonte devem ser listados após os códigos-fonte, cada um em sua própria linha. Todos os nomes de arquivo devem ser caminhos completos.

Por exemplo, em um projeto ConsoleApplication simples que tenha um arquivo de origem adicional Class1.cpp, o arquivo link.write.1.tlog pode conter:

^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

Build em tempo de design

No IDE, os projetos .vcxproj usam um conjunto de destinos do MSBuild para obter informações adicionais do projeto e regenerar arquivos de saída. Alguns desses destinos são usados apenas em builds em tempo de design, mas muitos deles são usados em builds regulares e builds em tempo de design.

Para obter informações gerais sobre builds em tempo de design, confira a documentação do CPS para Builds em tempo de design. Esta documentação é apenas parcialmente aplicável a projetos do Visual C++.

Os destinos CompileDesignTime e Compile mencionados na documentação de builds em tempo de design nunca são executados para projetos .vcxproj. Os projetos .vcxproj do Visual C++ usam destinos em tempo de design diferentes para obter informações do IntelliSense.

Destinos em tempo de design para informações do IntelliSense

Os destinos em tempo de design usados em projetos .vcxproj são definidos em $(VCTargetsPath)\Microsoft.Cpp.DesignTime.targets.

O destino GetClCommandLines coleta opções do compilador para o IntelliSense:

<Target
  Name="GetClCommandLines"
  Returns="@(ClCommandLines)"
  DependsOnTargets="$(DesignTimeBuildInitTargets);$(ComputeCompileInputsTargets)">
  • DesignTimeBuildInitTargets: destinos somente em tempo de design, necessários na inicialização de build em tempo de design. Entre outras coisas, esses destinos desabilitam algumas das funcionalidades de build regulares para melhorar o desempenho.

  • ComputeCompileInputsTargets: um conjunto de destinos que modifica opções e itens do compilador. Esses destinos são executados em builds regulares e em tempo de design.

O destino chama a tarefa CLCommandLine para criar a linha de comando a ser usada no IntelliSense. Novamente, apesar do nome, ele pode manipular não apenas opções de CL, mas também opções de Clang e gcc. O tipo das opções do compilador é controlado pela propriedade ClangMode.

Atualmente, a linha de comando produzida pela tarefa CLCommandLine sempre usa opções de CL (mesmo no modo Clang) porque são mais fáceis de serem analisadas pelo mecanismo do IntelliSense.

Se estiver adicionando um destino executado antes da compilação, seja regular ou em tempo de design, certifique-se de que ele não interrompa os builds em tempo de design nem afete o desempenho. A maneira mais simples de testar seu destino é abrir um Prompt de Comando do Desenvolvedor e executar este comando:

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

Esse comando produz um log de build detalhado, o msbuild.log, que tem um resumo de desempenho para os destinos e tarefas no final.

Certifique-se de usar Condition ="'$(DesignTimeBuild)' != 'true'" em todas as operações que só fazem sentido em builds regulares e não em builds em tempo de design.

Destinos em tempo de design que geram códigos-fonte

Esse recurso é desabilitado por padrão em projetos nativos da Área de Trabalho e, atualmente, não tem suporte em projetos armazenados em cache.

Se os metadados GeneratorTarget forem definidos para um item de projeto, o destino é executado automaticamente quando o projeto for carregado e quando o arquivo de origem for alterado.

Por exemplo, para gerar automaticamente arquivos .cpp ou .h usando arquivos .xaml, os arquivos $(VSInstallDir)\MSBuild\Microsoft\WindowsXaml\v16.0\*\Microsoft.Windows.UI.Xaml.CPP.Targets definem estas entidades:

<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>

Para usar Task.HostObject na obtenção do conteúdo não salvo de arquivos de origem, os destinos e a tarefa devem ser registrados como MsbuildHostObjects para os projetos fornecidos em um pkgdef:

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

Extensibilidade do projeto do Visual C++ no IDE do Visual Studio

O sistema de projeto do Visual C++ é baseado no VS Project System e usa seus pontos de extensibilidade. No entanto, a implementação da hierarquia do projeto é específica do Visual C++ e não baseada no CPS, portanto, a extensibilidade da hierarquia é limitada a itens de projeto.

Página de propriedades do projeto

Para obter informações gerais de design, confira Framework Multi-Targeting for VC++ Projects.

Em termos simples, as páginas de propriedades observadas na caixa de diálogo Propriedades do Projeto para um projeto em C++ são definidas por arquivos de regras. Um arquivo de regra especifica um conjunto de propriedades a serem mostradas em uma página de propriedades, além de como e onde elas devem ser salvas no arquivo de projeto. Os arquivos de regra são arquivos .xml que usam o formato Xaml. Os tipos usados na serialização deles são descritos em Microsoft.Build.Framework.XamlTypes. Para obter mais informações sobre o uso de arquivos de regra em projetos, confira Arquivos de regra XML da Página de Propriedades.

Os arquivos de regra devem ser adicionados ao grupo de itens PropertyPageSchema:

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

Os metadados Context limitam a visibilidade da regra, que também é controlada pelo tipo de regra, e podem ter um destes valores:

Project | File | PropertySheet

O CPS oferece suporte a outros valores para o tipo de contexto, mas eles não são usados em projetos do Visual C++.

Se a regra deve estar visível em mais de um contexto, use ponto-e-vírgula (;) para separar os valores de contexto, conforme mostrado aqui:

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

Formato da regra e tipos principais

O formato da regra é simples, portanto, esta seção descreve apenas os atributos que afetam a aparência dela na interface do usuário.

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

O atributo PageTemplate define como a regra é exibida na caixa de diálogo Páginas de Propriedades. O atributo pode ter um dos seguintes valores:

Atributo Descrição
generic Todas as propriedades são mostradas em uma página em Títulos de categoria
A regra pode ser visível nos contextos Project e PropertySheet, mas não no File.

Exemplo: $(VCTargetsPath)\1033\general.xml
tool As categorias são mostradas como subpáginas.
A regra pode ser visível em todos os contextos: Project, PropertySheet e File.
A regra fica visível em Propriedades do Projeto somente se o projeto tiver itens com o ItemType definido em Rule.DataSource, a menos que o nome da regra esteja incluído no grupo de itens ProjectTools.

Exemplo: $(VCTargetsPath)\1033\clang.xml
debugger A página é mostrada como parte da página Depuração.
Atualmente, as categorias são ignoradas.
O nome da regra deve corresponder ao atributo ExportDebugger do objeto MEF do Debug Launcher.

Exemplo: $(VCTargetsPath)\1033\debugger_local_windows.xml
personalizado Modelo personalizado. O nome do modelo deve corresponder ao atributo ExportPropertyPageUIFactoryProvider do objeto PropertyPageUIFactoryProvider do MEF. Confira Microsoft.VisualStudio.ProjectSystem.Designers.Properties.IPropertyPageUIFactoryProvider.

Exemplo: $(VCTargetsPath)\1033\userMacros.xml

Se a regra usar um dos modelos baseados na Grade de Propriedades, ela poderá utilizar estes pontos de extensibilidade nas propriedades:

Estender uma regra

Caso queira usar uma regra existente, mas precisa adicionar ou remover (ou seja, ocultar) apenas algumas propriedades, você pode criar uma Regra de extensão.

Substituir uma regra

Talvez você queira que o conjunto de ferramentas use a maioria das regras padrão do projeto, mas substitua apenas uma ou algumas delas. Por exemplo, digamos que você só queira alterar a regra em C ou C++ para mostrar diferentes opções de compilador. Você pode fornecer uma nova regra com o mesmo nome e nome de exibição da regra existente e incluí-la no grupo de itens PropertyPageSchema após a importação de destinos cpp padrão. Apenas uma regra com um nome próprio é usada no projeto, e a última incluída no grupo de itens PropertyPageSchema vence.

Itens do projeto

O arquivo ProjectItemsSchema.xml define os valores ContentType e ItemType para itens tratados como Itens de Projeto e define elementos FileExtension para determinar a qual grupo de itens um novo arquivo é adicionado.

O arquivo ProjectItemsSchema padrão é encontrado em $(VCTargetsPath)\1033\ProjectItemsSchema.xml. Para estendê-lo, você deve criar um arquivo de esquema com um novo nome, como 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>

Em seguida, no arquivo de destinos, adicione:

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

Exemplo: $(VCTargetsPath)\BuildCustomizations\masm.xml

Depuradores

O serviço de depuração no Visual Studio oferece suporte à extensibilidade do mecanismo de depuração. Para obter mais informações, confira estas amostras:

Para especificar os mecanismos de depuração e outras propriedades da sessão de depuração, você deve implementar um componente do MEF para o Debug Launcher e adicionar uma regra debugger. Para obter um exemplo, confira o arquivo $(VCTargetsPath)\1033\debugger_local_windows.xml.

Implantar

Os projetos .vcxproj usam a extensibilidade do sistema de projeto do Visual Studio para Provedores de Implantação.

Verificação atualizada do build

Por padrão, a verificação atualizada do build requer que os arquivos .tlog de leitura e gravação sejam criados na pasta $(TlogLocation) durante o build para todas as entradas e saídas de build.

Para usar uma verificação atualizada personalizada:

  1. Desabilite a verificação atualizada padrão adicionando a capacidade NoVCDefaultBuildUpToDateCheckProvider no arquivo Toolset.targets:

    <ItemGroup>
      <ProjectCapability Include="NoVCDefaultBuildUpToDateCheckProvider" />
    </ItemGroup>
    
  2. Implemente seu próprio IBuildUpToDateCheckProvider.

Atualização do projeto

Atualizador de projeto .vcxproj padrão

O atualizador de projeto .vcxproj padrão altera a versão do conjunto de ferramentas PlatformToolset, ApplicationTypeRevision, MSBuild e o .NET Framework. Os dois últimos são sempre alterados para os padrões da versão do Visual Studio, mas PlatformToolset e ApplicationTypeRevision podem ser controlados por propriedades especiais do MSBuild.

O atualizador usa estes critérios para decidir se um projeto pode ser atualizado ou não:

  1. Para projetos que definem ApplicationType e ApplicationTypeRevision, há uma pasta com um número de revisão maior do que o atual.

  2. A propriedade _UpgradePlatformToolsetFor_<safe_toolset_name> é definida para o conjunto de ferramentas atual e seu valor não é igual ao conjunto de ferramentas atual.

    Nesses nomes da propriedade, <safe_toolset_name> representa o nome do conjunto de ferramentas com todos os caracteres não alfanuméricos substituídos por um sublinhado (_).

Quando um projeto pode ser atualizado, ele participa do Redirecionamento de Solução. Para obter mais informações, confira IVsTrackProjectRetargeting2.

Caso queira adornar nomes de projeto no Gerenciador de Soluções quando os projetos usam um conjunto de ferramentas específico, defina uma propriedade _PlatformToolsetShortNameFor_<safe_toolset_name>.

Para obter exemplos de definições de propriedade _UpgradePlatformToolsetFor_<safe_toolset_name> e _PlatformToolsetShortNameFor_<safe_toolset_name>, confira o arquivo Microsoft.Cpp.Default.props. Para obter exemplos de uso, confira o arquivo $(VCTargetPath)\Microsoft.Cpp.Platform.targets.

Atualizador de projeto personalizado

Para usar um objeto atualizador de projeto personalizado, implemente um componente do MEF, conforme mostrado aqui:

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

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

Seu código pode importar e chamar o objeto atualizador .vcxproj padrão:

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

O IProjectRetargetHandler é definido em Microsoft.VisualStudio.ProjectSystem.VS.dll, sendo semelhante a IVsRetargetProjectAsync.

Defina a propriedade VCProjectUpgraderObjectName para dizer ao sistema de projeto que use o objeto atualizador personalizado:

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

Desabilitar a atualização de projeto

Para desabilitar as atualizações de projeto, use um valor NoUpgrade:

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

Cache e extensibilidade de projeto

Para melhorar o desempenho ao trabalhar com grandes soluções em C++ no Visual Studio 2017, o cache do projeto foi introduzido. Ele é implementado como um banco de dados SQLite preenchido com dados do projeto e, em seguida, usado para carregar projetos sem carregar projetos do MSBuild ou do CPS na memória.

Como não há objetos do CPS presentes em projetos .vcxproj carregados a partir do cache, não é possível criar os componentes do MEF da extensão que importam UnconfiguredProject ou ConfiguredProject. Para oferecer suporte à extensibilidade, o cache do projeto não é usado quando o Visual Studio detecta se um projeto usa (ou provavelmente usará) extensões do MEF.

Esses tipos de projeto são sempre totalmente carregados e têm objetos do CPS na memória, portanto, todas as extensões do MEF são criadas para eles:

  • Projetos de inicialização

  • Projetos que têm um atualizador de projeto personalizado, ou seja, definem uma propriedade VCProjectUpgraderObjectName

  • Projetos que não se destinam às janelas da área de trabalho, ou seja, definem uma propriedade ApplicationType

  • Projetos de itens compartilhados (.vcxitems) e quaisquer projetos que os referenciam por importação de projetos .vcxitems.

Se nenhuma dessas condições for detectada, um cache de projeto será criado. O cache inclui todos os dados do projeto do MSBuild necessários para responder a consultas get em interfaces VCProjectEngine. Isso significa que todas as modificações no nível do arquivo de objetos e destinos do MSBuild feitas por uma extensão devem funcionar apenas em projetos carregados do cache.

Envio da extensão

Para obter informações sobre como criar arquivos VSIX, confira Enviar extensões do Visual Studio. Para obter informações sobre como adicionar arquivos a localizações de instalação especiais, por exemplo, para adicionar arquivos em $(VCTargetsPath), confira Installing outside the extensions folder.

Recursos adicionais

O Microsoft Build System (MSBuild) fornece o mecanismo de build e o formato extensível baseado em XML para arquivos de projeto. Você deve se familiarizar com conceitos básicos do MSBuild e com o modo como o MSBuild para Visual C++ funciona para estender o sistema de projeto do Visual C++.

O Managed Extensibility Framework (MEF) fornece as APIs de extensão usadas pelo CPS e pelo sistema de projeto do Visual C++. Para obter uma visão geral de como o MEF é usado pelo CPS, confira CPS and MEF na visão geral do VSProjectSystem do MEF.

Você pode personalizar o sistema de build existente para adicionar etapas de build ou novos tipos de arquivo. Para obter mais informações, confira MSBuild (Visual C++) Overview e Working with project properties.