TFS Build Guide for SharePoint Projects

Important Note: This guide is meant to be a general overview and by no means is exhaustive. For detailed information about Site Definitions, Features and Solution Packages see the Windows SharePoint Services SDK (<msdn2.microsoft.com/en-us/library/ms441339.aspx>) and Chapter 9 of Ted Pattison’s book “Inside Windows SharePoint Services 3.0”.

Understanding SharePoint Solutions and Deployments

Site Definitions

A site definition is the top-level component in WSS that aggregates smaller, more modular definitions to create a complete site template that can be used to provision sites. For example, a site definition usually includes a custom page template for the site’s home page and can additionally reference external features to add support for other types of site-specific elements such as custom lists, secondary pages, and web parts. Creating custom site definitions enables you to develop site templates for creating new sits that act as prepackages business solutions.

Features

Features are the preferred packaging mechanism for SharePoint components. Features are easy to activate within existing sites and also have object and event models that are very powerful. A feature can be activated with a WSS site in five ways:

  • Site Settings Web Pages
  • STSADM command line
  • Site definition references
  • Feature activation dependencies
  • Feature stapling

Deployment Using Solution Packages

From Chapter 9 of Inside Windows SharePoint Services 3.0 by Ted Pattison:

Because SharePoint solutions are deployed on WSS or MOSS installations that range in size from a single standalone Web server to large enterprise server farms, there needs to be a mechanism to deploy them as a single unit. By deploying a single unit, we can have a supported, testable, and repeatable deployment mechanism. The deployment mechanism that SharePoint uses is the solution package.

Solution packages are critical components for deployment in enterprise scenarios and use the same Visual Studio project formats that you work with on your development box. For all deployments outside of your development environment, you want to use WSS solution packages. Solution packages enable your system administrators to create scriptable installs, an important requirement for Contoso.

It is also important to test solution package deployments in a controlled WSS environment (that is not the same as your development environment), such as a clean Virtual PC image, to test installation of features, site definitions, assemblies, and configuration changes using solution packages. Solution packages are generally language neutral without localized resources, supplemented by solution language packs that contain the language-specific resources.

clip_image002A solution package is a compressed .cab file with a .wsp extension containing the components to be deployed on a target web server. A solution package also contains additional metadata that enable the WSS runtime to uncompress the .cab file and install its components. In the case of server farm installations, WSS is able to automate pushing the solution package file out to each Web server in the farm.

Solution packages are deployed by using two steps. The first step is installation, in which WSs copies the .wsp file to the configuration database. The second step is the actual deployment, in which WSs creates a timer job that is processed by all front-end Web servers in the server farm. This greatly simplifies the installation across farm servers and ensures a consistent deployment.

The .wsp file for a solution package can be build using the MAKECAB operating system utility by reading the definition from a .ddf (Diamond Directive File) file. The .ddf file defines the output structure of the .wsp file be referencing each file in its source location and its destination location in the .wsp file. This is one of the more tedious aspects of WSS development because you will likely need to create and maintain the .ddf file by hand.

The metadata for a solution package is maintained in a file named manifest.xml that must be added to the root of the .wsp file. It is the manifest.xml file that tells the WSS runtime which template files to copy into the WSS sytem directories. The manifest.xml file can also instruct WSS to install features and assembly DLL as well as add entries to one or more web.config files for SafeControl entries and code access security settings.

An article on MSDN titled “Solution Deployment with SharePoint 2007” (msdn.microsoft.com/msdnmag/issues/07/08/officespace/default.aspx) provides a sample project and walks through creating a solution package. Figure 1 shows a sample project in Visual Studio.NET. Notice the two files in the Solution directory, Cab.ddf and manifest.xml. The file Cab.ddf, see Listing 1, defines which files are placed into the .cab file and their locations. The manifest.xml file instructs the WSS runtime how to read the files in the cab and how to create the files on the file system.

Listing 1: Sample DDF

    1:  .OPTION EXPLICIT ; Generate errors 
    2:  .Set CabinetNameTemplate=OfficeSpaceFeature.wsp
    3:  .Set DiskDirectory1=Package
    4:  .Set Cabinet=on
    5:  .Set MaxDiskSize=0
    6:  .Set CompressionType=MSZIP;
    7:  .Set DiskDirectoryTemplate=CDROM;
    8:   
    9:  Solution\manifest.xml manifest.xml
   10:   
   11:  TEMPLATE\FEATURES\OfficeSpaceFeature\feature.xml OfficeSpaceFeature\feature.xml
   12:  TEMPLATE\FEATURES\OfficeSpaceFeature\elements.xml OfficeSpaceFeature\elements.xml
   13:  TEMPLATE\FEATURES\OfficeSpaceFeature\LetterTemplate.docx OfficeSpaceFeature\LetterTemplate.docx
   14:  TEMPLATE\LAYOUTS\OfficeSpace\LetterGenerator.aspx LAYOUTS\OfficeSpace\LetterGenerator.aspx
   15:  TEMPLATE\IMAGES\OfficeSpace\PithHelmet.gif IMAGES\OfficeSpace\PithHelmet.gif
   16:   
   17:  bin\Debug\OfficeSpaceFeature.dll OfficeSpaceFeature.dll
   18:   
   19:  ; end of DDF file

Listing 2: Sample manifest.xml

    1:  <Solution 
    2:  SolutionId="{848ACD1F-DEBC-4078-B21D-56CECE3F499B}" 
    3:  xmlns="schemas.microsoft.com/sharepoint/">
    4:  <FeatureManifests>
    5:    <FeatureManifest Location="OfficeSpaceFeature\feature.xml" />
    6:  </FeatureManifests>
    7:  <TemplateFiles>
    8:   <TemplateFile Location="LAYOUTS\OfficeSpace\LetterGenerator.aspx" />
    9:   <TemplateFile Location="IMAGES\OfficeSpace\PithHelmet.gif"/>
   10:  </TemplateFiles>
   11:  <Assemblies>
   12:   <Assembly DeploymentTarget="GlobalAssemblyCache" Location="OfficeSpaceFeature.dll">
   13:  <!-- this project does not contain any web controls, but the SafeControl entry is here to demonstrate how to install a feature package when it contains elements that have to make modifications to the web.config file.
   14:  -->
   15:    <SafeControls>
   16:     <SafeControl Assembly="OfficeSpaceFeature, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31f10bb2f694edab" Namespace="OfficeSpaceFeature" TypeName="*" Safe="True" />
   17:     </SafeControls>
   18:    </Assembly>
   19:   </Assemblies>
   20:  </Solution>
   21:   

Configure Visual Studio to compile the .wsp

Visual Studio can be configured to compile the feature files into a .wsp whenever the project is built. The following steps show how to configure this.

Prerequisite
You need to download and install The MSBuild Community Tasks Project from msbuildtasks.tigris.org/. This includes a set of custom tasks for use in our project.

Create the SharePointFeaturePackage.targets file

MSBuild can be extended by creating .targets files and configuring the project to call specific targets in the file. In this case we will create a file names SharePointFeaturePackage.targets and configure the file the compile the project into a .wsp.

Start out by creating a new file in the solution by right-clicking on the solution name and select Add „ New Item.:

clip_image005

Select XML File and name it SharePointFeaturePackage.targets:

clip_image007

Create an XML element named Project as follows:

 <Project xmlns="schemas.microsoft.com/developer/msbuild/2003">

Because we plan on using the MSBuild Community Tasks in our solution, let’s add a reference to them in our .targets file:

 <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

The goal of this .targets file is to use the MakeCab utility to compress all the feature files into a single .wsp file. In order to do this we need to create a property that tells MSBuild where the utility resides in the file system. We also need to pass the output directory into MakeCab so that it knows where to place the compressed file. To accomplish this, we need to create an MSBuild PropertyGroup element and specify these properties:

 <PropertyGroup>
  <MAKECAB>"C:\Windows\System32\makecab.exe"</MAKECAB>
  <DiskDirectory1>"$(OutDir)"</DiskDirectory1>
  <DiskDirectory1 Condition="HasTrailingSlash($(OutDir))">"$(OutDir)."</DiskDirectory1>
</PropertyGroup>

Notice two important items about the DiskDirectory1 property. First, we set the default value of DiskDirectory1 to an MSBuild variable called OutDir. On a developer virtual machine, the OutDir variable will be either bin\debug\ or bin\release\. On the build server, OutDir will be the full path to the compilation directory, i.e. D:\DATA\TeamBuilds\Contoso.ACS.HS - Contoso Video Systems\Sample\Binaries\Debug\. Second, we conditionally add a trailing dot if the OutDir ends with a slash. This is required because MakeCab can’t handle the path ending with a slash.

With the properties in place, we are ready to create the MSBuild target for the .wsp creation. MakeCab makes uses the .ddf file created in your project, so make sure you have that in place before trying to compile the project. To create the target, create a new xml element called Target:

 <Target Name="SharePointFeaturePackage">

We next need to consider the path to any assemblies referenced by the DDF. The MakeCab utility allows us to pass parameters, however, I have been unable to correctly pass paths with spaces in them to MakeCab. To get around this, we need to modify the Cab.ddf file in order to set the correct path to the assemblies. We will use a custom MSBuild task from the MSBuild Community Tasks project to accomplish this. First make a copy of the original .ddf file:

 <Copy SourceFiles="DeploymentFiles\Cab.ddf" DestinationFiles="DeploymentFiles\WorkingCab.ddf" />

Next, let’s make sure our .ddf file has some tokens in it that we can replace with the correct path. Replace any hard-coded paths to your assembly (i.e. bin\debug\MyAssembly.dll MyAssembly.dll) with the following:

 #AssemblyFilePath# #AssemblyFileName# 

Now, back to our .targets file. After the Copy task we can use the custom FileUpdate task to replace the tokens with real values:

 <FileUpdate Files="DeploymentFiles\WorkingCab.ddf"
    Regex="#AssemblyFilePath# #AssemblyFileName#"
    ReplacementText="%22$(OutDir)$(AssemblyName).dll%22 %22$(AssemblyName).dll%22" />

Now, within the target we can use the Exec task to call the MakeCab utility:

 <Exec Command="$(MAKECAB) /F DeploymentFiles\WorkingCab.ddf /D CabinetNameTemplate=$(MSBuildProjectName).wsp /D DiskDirectory1=$(DiskDirectory1)" />

Additionally, I like to call the Exec task a second time when compiling in debug mode to create another version of the package with a .cab extension. This makes it much easier to look inside the file to see exactly what was added and the folder structure within it:

 <Exec Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU'"
    Command="$(MAKECAB) /F DeploymentFiles\WorkingCab.ddf /D CabinetNameTemplate=$(MSBuildProjectName).cab /D DiskDirectory1=$(DiskDirectory1)" />
Configure the Visual Studio project to call the SharePointFeaturePackage target

We now need to configure our Visual Studio project to call the SharePointFeaturePackage target after the project has been compiled. Alternatively, if the project does not contain any class files that need to be compiled, we can configure the project to call our custom target instead of compiling. To do this we have to edit the .csproj file and add import our .targets file. Since a .csproj file is just an MSBuild file, this process should look familiar.

Right-click the project name in Solution Explorer and select “Unload Project”:

clip_image009

Once the project has unloaded, right-click the project name in Solution Explorer and select “Edit [your project name].csproj”

clip_image011

Look for the <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> element and add the following right after it:

 <Import Project="SharePointFeaturePackage.targets" />

You may need to alter the path to the .targets file based on where on the file system you created the file.

Call the SharePointFeaturePackage target

If your project compiles C# code into an assembly, add the following into the AfterBuild target:

 <CallTarget Targets="SharePointFeaturePackage" />

If your project does not compile any code, change the DefaultTargets attribute of the Project elements to SharePointFeaturePackage:

 <Project ToolsVersion="3.0" 
    DefaultTargets="SharePointFeaturePackage" 
    xmlns="schemas.microsoft.com/developer/msbuild/2003">

Build your project and verify that both a .wsp and .cab file appear in the bin\debug or bin\release directory depending on the solution configuration selected.

Note: As additional projects are added, they can all use the same SharePointFeaturePackage.targets file. You simply need to change the Import element to supply the correct path to the .targets file. For example:

 <Import Project="..\OfficeSpaceFeatureCS\SharePointFeaturePackage.targets" />