Noções básicas sobre o processo de build
por Jason Lee
Este tópico fornece um passo a passo de um processo de build e implantação em escala empresarial. A abordagem descrita neste tópico usa arquivos de projeto de Microsoft Build Engine personalizados (MSBuild) para fornecer controle refinado sobre todos os aspectos do processo. Dentro dos arquivos de projeto, os destinos personalizados do MSBuild são usados para executar utilitários de implantação, como a Ferramenta de Implantação Web (MSDeploy.exe) dos Serviços de Informações da Internet (MSDeploy.exe) e o utilitário de implantação de banco de dados VSDBCMD.exe.
Observação
O tópico anterior, Understanding the Project File, descreveu os principais componentes de um arquivo de projeto do MSBuild e introduziu o conceito de dividir arquivos de projeto para dar suporte à implantação em vários ambientes de destino. Se você ainda não estiver familiarizado com esses conceitos, examine Noções básicas sobre o arquivo de projeto antes de trabalhar neste tópico.
Este tópico faz parte de uma série de tutoriais baseados nos requisitos de implantação corporativa de uma empresa fictícia chamada Fabrikam, Inc. Esta série de tutoriais usa uma solução de exemplo, a solução do Contact Manager, para representar um aplicativo Web com um nível realista de complexidade, incluindo um aplicativo ASP.NET MVC 3, um serviço WCF (Windows Communication Foundation) e um projeto de banco de dados.
O método de implantação no centro desses tutoriais baseia-se na abordagem de arquivo de projeto dividido descrita em Noções básicas sobre o Arquivo de Projeto, na qual o processo de build é controlado por dois arquivos de projeto, um contendo instruções de build que se aplicam a cada ambiente de destino e outro que contém configurações de build e implantação específicas do ambiente. No momento do build, o arquivo de projeto específico do ambiente é mesclado no arquivo de projeto independente do ambiente para formar um conjunto completo de instruções de build.
Visão geral de build e implantação
Na solução Do Gerenciador de Contatos, três arquivos controlam o processo de build e implantação:
- Um arquivo de projeto universal (Publish.proj). Isso contém instruções de build e implantação que não são alteradas entre ambientes de destino.
- Um arquivo de projeto específico do ambiente (Env-Dev.proj). Isso contém configurações de build e implantação específicas para um ambiente de destino específico. Por exemplo, você pode usar o arquivo Env-Dev.proj para fornecer configurações para um ambiente de desenvolvedor ou teste e criar um arquivo alternativo chamado Env-Stage.proj para fornecer configurações para um ambiente de preparo.
- Um arquivo de comando (Publish-Dev.cmd). Isso contém um comando MSBuild.exe que especifica quais arquivos de projeto você deseja executar. Você pode criar um arquivo de comando para cada ambiente de destino, em que cada arquivo contém um comando MSBuild.exe que especifica um arquivo de projeto específico do ambiente diferente. Isso permite que o desenvolvedor implante em ambientes diferentes simplesmente executando o arquivo de comando apropriado.
Na solução de exemplo, você pode encontrar esses três arquivos na pasta Publicar solução.
Antes de examinar esses arquivos com mais detalhes, vamos dar uma olhada em como o processo de build geral funciona quando você usa essa abordagem. Em um alto nível, o processo de build e implantação tem esta aparência:
A primeira coisa que acontece é que os dois arquivos de projeto, um que contém instruções universais de build e implantação e outro que contém configurações específicas do ambiente, são mesclados em um único arquivo de projeto. Em seguida, o MSBuild funciona por meio das instruções no arquivo de projeto. Ele cria cada um dos projetos na solução, usando o arquivo de projeto para cada projeto. Em seguida, ele chama outras ferramentas, como Implantação da Web (MSDeploy.exe) e o utilitário VSDBCMD para implantar seu conteúdo da Web e bancos de dados no ambiente de destino.
Do início ao fim, o processo de build e implantação executa estas tarefas:
Ele exclui o conteúdo do diretório de saída, em preparação para um novo build.
Ele cria cada projeto na solução:
- Para projetos Web, nesse caso, um aplicativo Web ASP.NET MVC e um serviço Web WCF, o processo de build cria um pacote de implantação da Web para cada projeto.
- Para projetos de banco de dados, o processo de build cria um manifesto de implantação (arquivo.deploymanifest) para cada projeto.
Ele usa o utilitário VSDBCMD.exe para implantar cada projeto de banco de dados na solução, usando várias propriedades dos arquivos de projeto, uma cadeia de conexão de destino e um nome de banco de dados, juntamente com o arquivo .deploymanifest.
Ele usa o utilitário MSDeploy.exe para implantar cada projeto Web na solução, usando várias propriedades dos arquivos de projeto para controlar o processo de implantação.
Você pode usar a solução de exemplo para rastrear esse processo com mais detalhes.
Observação
Para obter diretrizes sobre como personalizar os arquivos de projeto específicos do ambiente para seus próprios ambientes de servidor, consulte Configurar propriedades de implantação para um ambiente de destino.
Invocando o processo de build e implantação
Para implantar a solução Do Gerenciador de Contatos em um ambiente de teste do desenvolvedor, o desenvolvedor executa o arquivo de comando Publish-Dev.cmd . Isso invoca MSBuild.exe, especificando Publish.proj como o arquivo de projeto a ser executado e Env-Dev.proj como um valor de parâmetro.
msbuild.exe Publish.proj /fl /p:TargetEnvPropsFile=EnvConfig\Env-Dev.proj
Observação
O comutador /fl (abreviação de /fileLogger) registra a saída de build em um arquivo chamado msbuild.log no diretório atual. Para obter mais informações, consulte a Referência de Linha de Comando do MSBuild.
Neste ponto, o MSBuild começa a ser executado, carrega o arquivo Publish.proj e começa a processar as instruções dentro dele. A primeira instrução informa ao MSBuild para importar o arquivo de projeto especificado pelo parâmetro TargetEnvPropsFile .
<Import Project="$(TargetEnvPropsFile)" />
O parâmetro TargetEnvPropsFile especifica o arquivo Env-Dev.proj , portanto, o MSBuild mescla o conteúdo do arquivo Env-Dev.proj no arquivo Publish.proj .
Os próximos elementos que o MSBuild encontra no arquivo de projeto mesclado são grupos de propriedades. As propriedades são processadas na ordem em que aparecem no arquivo. O MSBuild cria um par chave-valor para cada propriedade, desde que todas as condições especificadas sejam atendidas. As propriedades definidas posteriormente no arquivo substituirão todas as propriedades com o mesmo nome definido anteriormente no arquivo. Por exemplo, considere as propriedades OutputRoot .
<OutputRoot Condition=" '$(OutputRoot)'=='' ">..\Publish\Out\</OutputRoot>
<OutputRoot Condition=" '$(BuildingInTeamBuild)'=='true' ">$(OutDir)</OutputRoot>
Quando o MSBuild processa o primeiro elemento OutputRoot , fornecendo um parâmetro de nome semelhante não foi fornecido, ele define o valor da propriedade OutputRoot como .. \Publish\Out. Quando encontrar o segundo elemento OutputRoot , se a condição for avaliada como true, ela substituirá o valor da propriedade OutputRoot pelo valor do parâmetro OutDir .
O próximo elemento que o MSBuild encontra é um único grupo de itens, contendo um item chamado ProjectsToBuild.
<ItemGroup>
<ProjectsToBuild Include="$(SourceRoot)ContactManager-WCF.sln"/>
</ItemGroup>
O MSBuild processa essa instrução criando uma lista de itens chamada ProjectsToBuild. Nesse caso, a lista de itens contém um único valor: o caminho e o nome do arquivo de solução.
Neste ponto, os elementos restantes são destinos. Os destinos são processados de forma diferente das propriedades e dos itens— essencialmente, os destinos não são processados, a menos que sejam especificados explicitamente pelo usuário ou invocados por outro constructo dentro do arquivo de projeto. Lembre-se de que a marca de abertura do Projeto inclui um atributo DefaultTargets .
<Project ToolsVersion="4.0"
DefaultTargets="FullPublish"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Isso instrui o MSBuild a invocar o destino FullPublish se os destinos não forem especificados quando MSBuild.exe for invocado. O destino FullPublish não contém nenhuma tarefa; em vez disso, ele simplesmente especifica uma lista de dependências.
<PropertyGroup>
<FullPublishDependsOn>
Clean;
BuildProjects;
GatherPackagesForPublishing;
PublishDbPackages;
PublishWebPackages;
</FullPublishDependsOn>
</PropertyGroup>
<Target Name="FullPublish" DependsOnTargets="$(FullPublishDependsOn)" />
Essa dependência informa ao MSBuild que, para executar o destino FullPublish , ele precisa invocar essa lista de destinos na ordem fornecida:
- Ele deve invocar o destino Limpar .
- Ele deve invocar o destino BuildProjects .
- Ele deve invocar o destino GatherPackagesForPublishing .
- Ele deve invocar o destino PublishDbPackages .
- Ele deve invocar o destino PublishWebPackages .
O destino limpo
O destino Clean basicamente exclui o diretório de saída e todo o seu conteúdo, como preparação para um novo build.
<Target Name="Clean" Condition=" '$(BuildingInTeamBuild)'!='true' ">
<Message Text="Cleaning up the output directory [$(OutputRoot)]"/>
<ItemGroup>
<_FilesToDelete Include="$(OutputRoot)**\*"/>
</ItemGroup>
<Delete Files="@(_FilesToDelete)"/>
<RemoveDir Directories="$(OutputRoot)"/>
</Target>
Observe que o destino inclui um elemento ItemGroup . Ao definir propriedades ou itens dentro de um elemento Target , você está criando propriedades e itens dinâmicos . Em outras palavras, as propriedades ou itens não são processados até que o destino seja executado. O diretório de saída pode não existir ou conter arquivos até que o processo de build seja iniciado, portanto, você não poderá compilar a lista _FilesToDelete como um item estático; você precisa aguardar até que a execução esteja em andamento. Dessa forma, você cria a lista como um item dinâmico dentro do destino.
Observação
Nesse caso, como o destino Limpar é o primeiro a ser executado, não há necessidade real de usar um grupo de itens dinâmicos. No entanto, é uma boa prática usar propriedades e itens dinâmicos nesse tipo de cenário, pois talvez você queira executar destinos em uma ordem diferente em algum momento.
Você também deve tentar evitar declarar itens que nunca serão usados. Se você tiver itens que serão usados apenas por um destino específico, considere colocá-los dentro do destino para remover qualquer sobrecarga desnecessária no processo de build.
Itens dinâmicos à parte, o destino Limpar é bastante simples e usa as tarefas internas Message, Delete e RemoveDir para:
- Envie uma mensagem para o agente.
- Crie uma lista de arquivos a serem excluídos.
- Excluir os arquivos.
- Remova o diretório de saída.
O destino BuildProjects
O destino BuildProjects basicamente cria todos os projetos na solução de exemplo.
<Target Name="BuildProjects" Condition=" '$(BuildingInTeamBuild)'!='true' ">
<MSBuild Projects="@(ProjectsToBuild)"
Properties="OutDir=$(OutputRoot);
Configuration=$(Configuration);
DeployOnBuild=true;
DeployTarget=Package"
Targets="Build" />
</Target>
Esse destino foi descrito em alguns detalhes no tópico anterior, Noções básicas sobre o arquivo de projeto, para ilustrar como tarefas e destinos referenciam propriedades e itens. Neste ponto, você está interessado principalmente na tarefa MSBuild . Você pode usar essa tarefa para criar vários projetos. A tarefa não cria uma nova instância de MSBuild.exe; ele usa a instância em execução atual para criar cada projeto. Os principais pontos de interesse neste exemplo são as propriedades de implantação:
- A propriedade DeployOnBuild instrui o MSBuild a executar quaisquer instruções de implantação nas configurações do projeto quando a compilação de cada projeto for concluída.
- A propriedade DeployTarget identifica o destino que você deseja invocar depois que o projeto é criado. Nesse caso, o destino pacote compila a saída do projeto em um pacote Web implantável.
Observação
O destino do pacote invoca o WPP (Web Publishing Pipeline), que fornece integração entre o MSBuild e a Implantação da Web. Se você quiser dar uma olhada nos destinos internos que o WPP fornece, examine o arquivo Microsoft.Web.Publishing.targets na pasta %PROGRAMFILES(x86)%\MSBuild\Microsoft\VisualStudio\v10.0\Web.
O destino GatherPackagesForPublishing
Se você estudar o destino GatherPackagesForPublishing , observará que ele não contém nenhuma tarefa. Em vez disso, ele contém um único grupo de itens que define três itens dinâmicos.
<Target Name="GatherPackagesForPublishing">
<ItemGroup>
<PublishPackages
Include="$(_ContactManagerDest)ContactManager.Mvc.deploy.cmd">
<WebPackage>true</WebPackage>
<!-- More item metadata -->
</PublishPackages>
<PublishPackages
Include="$(_ContactManagerSvcDest)ContactManager.Service.deploy.cmd">
<WebPackage>true</WebPackage>
<!-- More item metadata -->
</PublishPackages>
<DbPublishPackages Include="$(_DbDeployManifestPath)">
<DbPackage>true</DbPackage>
<!-- More item metadata -->
</DbPublishPackages>
</ItemGroup>
</Target>
Esses itens referem-se aos pacotes de implantação que foram criados quando o destino BuildProjects foi executado. Não foi possível definir esses itens estaticamente no arquivo de projeto, pois os arquivos aos quais os itens se referem não existem até que o destino BuildProjects seja executado. Em vez disso, os itens devem ser definidos dinamicamente dentro de um destino que não é invocado até que o destino BuildProjects seja executado.
Os itens não são usados nesse destino— esse destino simplesmente cria os itens e os metadados associados a cada valor de item. Depois que esses elementos forem processados, o item PublishPackages conterá dois valores, o caminho para o arquivo ContactManager.Mvc.deploy.cmd e o caminho para o arquivo ContactManager.Service.deploy.cmd . A Implantação da Web cria esses arquivos como parte do pacote da Web para cada projeto, e esses são os arquivos que você deve invocar no servidor de destino para implantar os pacotes. Se você abrir um desses arquivos, basicamente verá um comando MSDeploy.exe com vários valores de parâmetro específicos do build.
O item DbPublishPackages conterá um único valor, o caminho para o arquivo ContactManager.Database.deploymanifest .
Observação
Um arquivo .deploymanifest é gerado quando você cria um projeto de banco de dados e usa o mesmo esquema que um arquivo de projeto do MSBuild. Ele contém todas as informações necessárias para implantar um banco de dados, incluindo o local do esquema de banco de dados (.dbschema) e detalhes de qualquer script de pré-implantação e pós-implantação. Para obter mais informações, consulte Uma visão geral da compilação e implantação do banco de dados.
Você aprenderá mais sobre como os pacotes de implantação e os manifestos de implantação de banco de dados são criados e usados na criação e empacotamento de projetos de aplicativo Web e implantação de projetos de banco de dados.
O destino PublishDbPackages
Resumidamente, o destino PublishDbPackages invoca o utilitário VSDBCMD para implantar o banco de dados ContactManager em um ambiente de destino. Configurar a implantação de banco de dados envolve muitas decisões e nuances, e você aprenderá mais sobre isso em Implantando projetos de banco de dados e personalizando implantações de banco de dados para vários ambientes. Neste tópico, vamos nos concentrar em como esse destino realmente funciona.
Primeiro, observe que a marca de abertura inclui um atributo Outputs .
<Target Name="PublishDbPackages" Outputs="%(DbPublishPackages.Identity)">
Este é um exemplo de envio em lote de destino. Em arquivos de projeto do MSBuild, o envio em lote é uma técnica para iterar em coleções. O valor do atributo Outputs , "%(DbPublishPackages.Identity)", refere-se à propriedade de metadados Identity da lista de itens DbPublishPackages . Essa notação, Outputs=%(ItemList.ItemMetadataName), é traduzida como:
- Divida os itens em DbPublishPackages em lotes de itens que contêm o mesmo valor de metadados de identidade .
- Execute o destino uma vez por lote.
Observação
A identidade é um dos valores de metadados internos atribuídos a cada item na criação. Ele se refere ao valor do atributo Include no elemento Item , em outras palavras, o caminho e o nome do arquivo do item.
Nesse caso, como nunca deve haver mais de um item com o mesmo caminho e nome de arquivo, estamos essencialmente trabalhando com tamanhos de lote de um. O destino é executado uma vez para cada pacote de banco de dados.
Você pode ver uma notação semelhante na propriedade _Cmd , que cria um comando VSDBCMD com as opções apropriadas.
<_Cmd>"$(VsdbCmdExe)"
/a:Deploy
/cs:"%(DbPublishPackages.DatabaseConnectionString)"
/p:TargetDatabase=%(DbPublishPackages.TargetDatabase)
/manifest:"%(DbPublishPackages.FullPath)"
/script:"$(_CmDbScriptPath)"
$(_DbDeployOrScript)
</_Cmd>
Nesse caso, %(DbPublishPackages.DatabaseConnectionString), %(DbPublishPackages.TargetDatabase)e %(DbPublishPackages.FullPath) referem-se a valores de metadados da coleção de itens DbPublishPackages . A propriedade _Cmd é usada pela tarefa Exec , que invoca o comando .
<Exec Command="$(_Cmd)"/>
Como resultado dessa notação, a tarefa Exec criará lotes com base em combinações exclusivas dos valores de metadados DatabaseConnectionString, TargetDatabase e FullPath , e a tarefa será executada uma vez para cada lote. Este é um exemplo de envio em lote de tarefas. No entanto, como o envio em lote no nível de destino já dividiu nossa coleção de itens em lotes de item único, a tarefa Exec será executada uma e apenas uma vez para cada iteração do destino. Em outras palavras, essa tarefa invoca o utilitário VSDBCMD uma vez para cada pacote de banco de dados na solução.
Observação
Para obter mais informações sobre o envio em lote de destino e tarefa, consulte Envio em lote do MSBuild, metadados de item em lote de destino e metadados de item no envio em lote de tarefas.
O destino PublishWebPackages
A essa altura, você invocou o destino BuildProjects , que gera um pacote de implantação da Web para cada projeto na solução de exemplo. Acompanhar cada pacote é um arquivo deploy.cmd , que contém os comandos MSDeploy.exe necessários para implantar o pacote no ambiente de destino e um arquivo SetParameters.xml , que especifica os detalhes necessários do ambiente de destino. Você também invocou o destino GatherPackagesForPublishing , que gera uma coleção de itens que contém os arquivos deploy.cmd nos quais você está interessado. Essencialmente, o destino PublishWebPackages executa estas funções:
- Ele manipula o arquivo deSetParameters.xml para cada pacote para incluir os detalhes corretos para o ambiente de destino, usando a tarefa XmlPoke .
- Ele invoca o arquivo deploy.cmd para cada pacote, usando as opções apropriadas.
Assim como o destino PublishDbPackages , o destino PublishWebPackages usa o envio em lote de destino para garantir que o destino seja executado uma vez para cada pacote da Web.
<Target Name="PublishWebPackages" Outputs="%(PublishPackages.Identity)">
Dentro do destino, a tarefa Exec é usada para executar o arquivo deploy.cmd para cada pacote da Web.
<PropertyGroup>
<_Cmd>
%(PublishPackages.FullPath)
$(_WhatifSwitch)
/M:$(MSDeployComputerName)
%(PublishPackages.AdditionalMSDeployParameters)
</_Cmd>
</PropertyGroup>
<Exec Command="$(_Cmd)"/>
Para obter mais informações sobre como configurar a implantação de pacotes Web, consulte Compilando e empacotando projetos de aplicativo Web.
Conclusão
Este tópico forneceu um passo a passo de como os arquivos de projeto divididos são usados para controlar o processo de build e implantação do início ao fim para a solução de exemplo do Contact Manager. O uso dessa abordagem permite executar implantações complexas em escala empresarial em uma única etapa repetível, simplesmente executando um arquivo de comando específico do ambiente.
Leitura Adicional
Para obter uma introdução mais detalhada aos arquivos de projeto e ao WPP, consulte Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build by Sayed Ibrahim Hashimi and William Bartholomew, ISBN: 978-0-7356-4524-0.