Understanding the Build Process
5. It must invoke the PublishWebPackages target
The Clean Target
The Clean target basically deletes the output directory and all its contents, as preparation for a fresh build.
XML
<Target Name="Clean" Condition=" '$(BuildingInTeamBuild)'!='true' ">
<Message Text="Cleaning up the output directory [$(OutputRoot)]"/>
46 <ItemGroup>
<_FilesToDelete Include="$(OutputRoot)**\*"/>
</ItemGroup>
<Delete Files="@(_FilesToDelete)"/>
<RemoveDir Directories="$(OutputRoot)"/>
</Target>
Notice that the target includes an ItemGroup element. When you define properties or items within a Target element, you’re creating dynamic properties and items. In other words, the properties or items aren't processed until the target is executed. The output directory might not exist or contain any files until the build process begins, so you can't build the _FilesToDelete list as a static item; you have to wait until execution is underway. As such, you build the list as a dynamic item within the target.
Note: In this case, because the Clean target is the first to be executed, there's no real need to use a dynamic item group. However, it's good practice to use dynamic properties and items in this type of scenario, as you might want to execute targets in a different order at some point.
You should also aim to avoid declaring items that will never be used. If you have items that will only be used by a specific target, consider placing them inside the target to remove any unnecessary overhead on the build process.
Dynamic items aside, the Clean target is fairly straightforward and makes use of the built-in Message, Delete, and RemoveDir tasks to:
1. Send a message to the logger.
2. Build a list of files to delete.
3. Delete the files.
4. Remove the output directory.
The BuildProjects Target
The BuildProjects target basically builds all the projects in the sample solution.
XML
<Target Name="BuildProjects" Condition=" '$(BuildingInTeamBuild)'!='true' ">
<MSBuild Projects="@(ProjectsToBuild)"
Properties="OutDir=$(OutputRoot);
This target was described in some detail in the previous topic, Understanding the Project File, to
illustrate how tasks and targets reference properties and items. At this point, you're mainly interested in the MSBuild task. You can use this task to build multiple projects. The task does not create a new
47
instance of MSBuild.exe; it uses the current running instance to build each project. The key points of interest in this example are the deployment properties:
The DeployOnBuild property instructs MSBuild to run any deployment instructions in the project settings when the build of each project is complete.
The DeployTarget property identifies the target that you want to invoke after the project is built. In this case, the Package target builds the project output into a deployable web package.
Note: The Package target invokes the Web Publishing Pipeline (WPP), which provides integration between MSBuild and Web Deploy. If you want to take a look at the built-in targets that the WPP provides, review the Microsoft.Web.Publishing.targets file in the
%PROGRAMFILES(x86)%\MSBuild\Microsoft\VisualStudio\v10.0\Web folder.
The GatherPackagesForPublishing Target
If you study the GatherPackagesForPublishing target, you'll notice that it doesn't actually contain any tasks. Instead, it contains a single item group that defines three dynamic items.
XML
<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>
These items refer to the deployment packages that were created when the BuildProjects target was executed. You couldn't define these items statically in the project file, because the files to which the items refer don't exist until the BuildProjects target is executed. Instead, the items must be defined dynamically within a target that is not invoked until after the BuildProjects target is executed.
The items are not used within this target—this target simply builds the items and the metadata associated with each item value. Once these elements are processed, the PublishPackages item will contain two values, the path to the ContactManager.Mvc.deploy.cmd file and the path to the
48
ContactManager.Service.deploy.cmd file. Web Deploy creates these files as part of the web package for each project, and these are the files that you must invoke on the destination server in order to deploy the packages. If you open up one of these files, you'll basically see an MSDeploy.exe command with various build-specific parameter values.
The DbPublishPackages item will contain a single value, the path to the ContactManager.Database.deploymanifest file.
Note: A .deploymanifest file is generated when you build a database project, and it uses the same schema as an MSBuild project file. It contains all the information required to deploy a database, including the location of the database schema (.dbschema) and details of any pre-deployment and post-deployment scripts. For more information, see An Overview of Database Build and Deployment.
You'll learn more about how deployment packages and database deployment manifests are created and used in Building and Packaging Web Application Projects and Deploying Database Projects.
The PublishDbPackages Target
Briefly speaking, the PublishDbPackages target invokes the VSDBCMD utility to deploy the
ContactManager database to a target environment. Configuring database deployment involves lots of decisions and nuances, and you'll learn more about this in Deploying Database Projects and Customizing Database Deployments for Multiple Environments. In this topic, we'll focus on how this target actually functions.
First, notice that the opening tag includes an Outputs attribute.
XML
<Target Name="PublishDbPackages" Outputs="%(DbPublishPackages.Identity)">
This is an example of target batching. In MSBuild project files, batching is a technique for iterating over collections. The value of the Outputs attribute, "%(DbPublishPackages.Identity)", refers to the Identity metadata property of the DbPublishPackages item list. This notation,
Outputs=%(ItemList.ItemMetadataName), is translated as:
Split the items in DbPublishPackages into batches of items that contain the same Identity metadata value.
Execute the target once per batch.
Note: Identity is one of the built-in metadata values that is assigned to every item on creation. It refers to the value of the Include attribute in the Item element—in other words, the path and filename of the item.
In this case, because there should never be more than one item with the same path and filename, we're essentially working with batch sizes of one. The target is executed once for every database package.
49
You can see a similar notation in the _Cmd property, which builds a VSDBCMD command with the appropriate switches.
XML
<_Cmd>"$(VsdbCmdExe)"
/a:Deploy
/cs:"%(DbPublishPackages.DatabaseConnectionString)"
/p:TargetDatabase=%(DbPublishPackages.TargetDatabase) /manifest:"%(DbPublishPackages.FullPath)"
/script:"$(_CmDbScriptPath)"
$(_DbDeployOrScript)
</_Cmd>
In this case, %(DbPublishPackages.DatabaseConnectionString),
%(DbPublishPackages.TargetDatabase), and %(DbPublishPackages.FullPath) all refer to metadata values of the DbPublishPackages item collection. The _Cmd property is used by the Exec task, which invokes the command.
XML
<Exec Command="$(_Cmd)"/>
As a result of this notation, the Exec task will create batches based on unique combinations of the DatabaseConnectionString, TargetDatabase, and FullPath metadata values, and the task will execute once for each batch. This is an example of task batching. However, because the target-level batching has already divided our item collection into single-item batches, the Exec task will run once and only once for each iteration of the target. In other words, this task invokes the VSDBCMD utility once for each database package in the solution.
Note: For more information on target and task batching, see MSBuild Batching, Item Metadata in Target Batching, and Item Metadata in Task Batching.
The PublishWebPackages Target
By this point, you've invoked the BuildProjects target, which generates a web deployment package for each project in the sample solution. Accompanying each package is a deploy.cmd file, which contains the MSDeploy.exe commands required to deploy the package to the target environment, and a
SetParameters.xml file, which specifies the necessary details of the target environment. You've also invoked the GatherPackagesForPublishing target, which generates an item collection containing the deploy.cmd files you're interested in. Essentially, the PublishWebPackages target performs these functions:
It manipulates the SetParameters.xml file for each package to include the correct details for the target environment, using the XmlPoke task.
It invokes the deploy.cmd file for each package, using the appropriate switches.
50
Just like the PublishDbPackages target, the PublishWebPackages target uses target batching to ensure that the target is executed once for each web package.
XML
<Target Name="PublishWebPackages" Outputs="%(PublishPackages.Identity)">
Within the target, the Exec task is used to run the deploy.cmd file for each web package.
XML
%(PublishPackages.AdditionalMSDeployParameters) </_Cmd>
</PropertyGroup>
<Exec Command="$(_Cmd)"/>
For more information on configuring the deployment of web packages, see Building and Packaging Web Application Projects.
Conclusion
This topic provided a walkthrough of how split project files are used to control the build and deployment process from start to finish for the Contact Manager sample solution. Using this approach lets you run complex, enterprise-scale deployments in a single, repeatable step, simply by running an environment-specific command file.
Further Reading
For a more in-depth introduction to project files and the WPP, see Inside the Microsoft Build Engine:
Using MSBuild and Team Foundation Build by Sayed Ibrahim Hashimi and William Bartholomew, ISBN:
978-0-7356-4524-0.