Automate SharePoint Solution Builds with Visual Studio Extensions For Windows SharePoint Services (VSEWSS) 1.3


In this walkthrough, I’ll demonstrate how to achieve automated builds and continuous integration by creating a build script for VSEWSS 1.3 solutions.  The patterns and practices SharePoint Guidance  (11 drop) contains an example build script that this article is based on, but I found some issues with it in practice that I’d like to share.  Before we get started, I’d like to talk about the design of the extensions and some of the problems you may encounter with them. 


VSEWSS 1.3 stores information used to build the wsp file in a directory named “pkg” that is not included in the project.  This works fine for individual development on a single machine, but not so well for team development and build automation.  The pkg directory must be manually included in the project so files needed to build the wsp are included in source control.  If you forget to do this, your teammates won’t be able to build the wsp, and you risk losing deployment configuration.  Furthermore, if you later add a new feature, you also have manually add the new feature subdirectory to the project.  In addition to storing configuration information, the pkg directory is also where solution files (feature.xml for example) are automatically generated.  When you package the wsp (or refresh in the wsp view), the files in the pkg directory must be checked out to be re-generated.  This also causes problems on the build server because the files in the pkg directory are read-only because they are checked into source control.  An improved design would be to store wsp configuration information in a folder/file that is part of the project and use the pkg directory only for generating packages based on the wsp configuration files.  So now that you are aware of some of the team development “gotchas” with VSEWSS 1.3, I’ll show you, step-by-step, how to configure automated builds.


If you just want to see the build script, get it hereUpdate: get a simplified build script here.


If you have not installed TFS Build, you will need to install it using the TFS installation media.  You will also need to configure a service account such as TFSBuild, and add the account to the TFS server group Team Foundation Licensed Users and the Team Project(s) group Build Services.


To get started, configure the build agent.  In the team project select Builds > Manage Build Agents…


Manage Build Agents


Enter the build agent properties for your build server


Build Agent Properties


Create a new build definition by selecting Builds > New Build Definition…


New Build Definition


Enter a name for the build definition


Build Definition General 


Configure the workspace.  Note: all files in the source control folder will be downloaded to the local folder you specify, so be sure the location you specify has enough free space


Build Definition Workspace


Create the project file (TFSBuild.proj) by selecting the version control folder where you want it stored and selecting Create…


Build Definition Project File


Select the Visual Studio solution to build and click Next


MSBuild Project File Creation Wizard Selections


Select the configuration(s) to build and click Next


MSBuild Project File Creation Wizard Configurations


If you want to run unit tests or code analysis during the build, configure it and click Finish


MSBuild Project File Creation Wizard Options


Now that the project file is created, click OK


Build Definition Project File


Configure how many builds you would like to retain and click OK.  Note: you should at least keep the latest of failed or partially succeeded builds or the build log file you need to troubleshoot will be deleted


Build Definition Retention Policy


Enter a share where the build files will be dropped and click OK


Build Definition Defaults


Configure what triggers the build, either manually, on a schedule or when files are checked in (continuous integration)


Build Definition Trigger


Test the build by highlighting the build definition and selecting Queue New Build…


Queue New Build


Click Queue


Queue Build


At this point you should have a working build, but it doesn’t package the wsp file yet


Build Results


Next, we’ll modify the build script to package the wsp file.  The build steps are as follows:



  1. Clean up any workspaces / files left by the previous build packaging
  2. Build the Visual Studio solution
  3. Delete the workspace that was created by the build
  4. Create a workspace for the wsp packaging build
  5. Open a second instance of the IDE and package the solution using the /package switch
  6. Copy the wsp to the drop folder

In addition to this process, a nice to have is to display the packaging steps in the GUI, so we’ll use the BuildStep task to accomplish that


To get started, get the latest version of the Team Build Types folder


Get Latest Version Team Build Types


Check out the TFSBuild.proj file.  Note: when developing the build script, you will have to check it out to work on it, and check it back in to test it


Check Out TFSBuild.proj


Add the custom tasks used by the build.  Although we could accomplish what we need to using TF commands, these tasks are installed with TFS and work well

<Project DefaultTargets=”DesktopBuild” xmlns=”http://schemas.microsoft.com/developer/msbuild/2003″ ToolsVersion=”3.5″>

<!– These tasks are used by the Team Build process defined in this file –>
<UsingTask TaskName=”Microsoft.TeamFoundation.Build.Tasks.DeleteWorkspaceTask”
AssemblyFile=”$(TeamBuildRefPath)\Microsoft.TeamFoundation.Build.Tasks.VersionControl.dll” />
<UsingTask TaskName=”Microsoft.TeamFoundation.Build.Tasks.CreateWorkspaceTask”
AssemblyFile=”$(TeamBuildRefPath)\Microsoft.TeamFoundation.Build.Tasks.VersionControl.dll” />


Add the variables needed for wsp packaging.  Note: there is probably a better way to determine the location of the IDE

  <PropertyGroup>

<!–
Variables added for VSeWSS 1.3 builds
–>

<!– IDEPath
The path in which DevEnv and TF reside
–>
<IDEPath>C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE</IDEPath>

<!– TempWorkspaceName
Workspace name, must not match an existing namespace name
–>
<TempWorkspaceName>NightlyBuildTempWorkspace</TempWorkspaceName>

</PropertyGroup>


Add the solution to build.  Note: this will build the binaries, but will not generate the wsp file

  <ItemGroup>

<SolutionToBuild Include=”$(BuildProjectFolderPath)/../../Intranet/Intranet.sln”>
<Targets></Targets>
<Properties></Properties>
</SolutionToBuild>

</ItemGroup>


Add the target to clean up any files left by the previous build packaging (if the previous build failed or was cancelled).  This target is called during the build just before the workspace is created to build the solution

  <!– Before the build workpace is initialized –>
<Target Name=”BeforeInitializeWorkspace” >

<!– Delete temporary workspace (if left by previous build) –>
<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Message=”Deleting temporary workspace &quot;$(TempWorkspaceName)&quot; (if left by previous build).”>
<Output TaskParameter=”Id” PropertyName=”StepId” />
</BuildStep>

<DeleteWorkspaceTask
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Name=”$(TempWorkspaceName)”
DeleteLocalItems=”true” />

<DeleteWorkspaceTask
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Name=”$(TempWorkspaceName)”
DeleteLocalItems=”false” />

<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Id=”$(StepId)”
Status=”Succeeded” />

<!– Error Occurred –>
<OnError ExecuteTargets=”MarkBuildStepAsFailed” />

</Target>


Add the error handling target.  If any of the other steps fail, they will call this target using ExecuteTargets

    <!– Handles custom errors –>
<Target Name=”MarkBuildStepAsFailed”>
<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Id=”$(StepId)”
Status=”Failed” />
</Target>

In the AfterCompile target, add the step to delete the workspace that was automatically created by the build

<!– After the solutions are compiled –>
<Target Name=”AfterCompile” >

<!– Delete build workspace –>
<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Message=”Deleting build workspace &quot;$(WorkspaceName)&quot;.”>
<Output TaskParameter=”Id” PropertyName=”StepId” />
</BuildStep>

<DeleteWorkspaceTask
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Name=”$(WorkspaceName)”
DeleteLocalItems=”false” />

<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Id=”$(StepId)”
Status=”Succeeded” />
<!– END Delete build workspace –>


Just below that, add the build step to create the temporary workspace

    <!– Create temporary workspace –>
<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Message=”Creating temporary workspace &quot;$(TempWorkspaceName)&quot;.”>
<Output TaskParameter=”Id” PropertyName=”StepId” />
</BuildStep>

<CreateWorkspaceTask
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
BuildDirectory=”$(BuildDirectory)”
SourcesDirectory=”$(SolutionRoot)”
Name=”$(TempWorkspaceName)”
Comment=”Temporary workspace”>
</CreateWorkspaceTask>

<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Id=”$(StepId)”
Status=”Succeeded” />
<!– END Create temporary workspace –>


Add the step to package the solution.  This first marks all files in the pkg directory as not read only–the packaging will not be able to overwrite these files otherwise.  Then it runs the IDE and uses the /package switch to generate the wsp.  Finally, it copies the wsp to the drop folder.  If you had multiple wsp files to build, you could repeat the steps below or refactor into a target

    <!– Place projects to package here –>

<!– Build and package solution –>
<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Message=”Packaging &quot; Contoso Intranet solution&quot;.”>
<Output TaskParameter=”Id” PropertyName=”StepId” />
</BuildStep>

<!– The extensions modify files in the pkg directory, so those files cannot read only–>
<Exec Command=”attrib -R &quot;$(BuildDirectory)\Contoso\Intranet\Deployment\pkg\*.*&quot; /S /D” />

<!– Open a second instance of the dev environment and build using /package switch –>
<Exec Command=”&quot;$(IDEPath)\devenv&quot; &quot;$(BuildDirectory)\Contoso\Intranet\Intranet.sln&quot; /deploy debug /package” />

<!– Copy to drop location –>
<Exec Command=”xcopy &quot;$(BuildDirectory)\Contoso\Intranet\Deployment\bin\debug\Contoso.Intranet.wsp&quot; &quot;$(DropLocation)\$(BuildNumber)\&quot; /E /Y /R” />

<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Id=”$(StepId)”
Status=”Succeeded” />
<!– END Build and package solution –>


Add the build step to delete the temporary workspace we created

    <!– Delete temporary workspace –>
<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Message=”Deleting temporary workspace &quot;$(TempWorkspaceName)&quot;.”>
<Output TaskParameter=”Id” PropertyName=”StepId” />
</BuildStep>

<DeleteWorkspaceTask
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Name=”$(TempWorkspaceName)”
DeleteLocalItems=”false” />

<BuildStep
TeamFoundationServerUrl=”$(TeamFoundationServerUrl)”
BuildUri=”$(BuildUri)”
Id=”$(StepId)”
Status=”Succeeded” />
<!– END Delete temporary workspace –>


Check the project back in to source control, and queue the build to test it

Comments (8)

  1. It seems every week more and more great content appears! These could almost become daily Coffee break posts! WSP Carsten Keutmann: Beginning SharePoint Development (Bonus part #3 WSPBuilder overview)

  2. Sandeep says:

    Important thing to mention here is that this will only work if your team has TFS Build Server…

  3. Craig says:

    This is great information, but I have Visual Source Safe 6.0.  Can somebody please give an example of how a build script could be done using Visual Studio 2008 and Visual Source Safe?

  4. Philipp Schmid says:

    Do I have to install and configure VSeWSS 1.3 on the build machine? I am a bit concerned about the IIS modifications needed to get VSeWSS 1.3 running on a dev machine. Since the build doesn’t need to deploy I wonder what is needed.

    XCopy steps for configuring the build server to build VSeWSS 1.3 projects would be greatly appreciated!

    Philipp

  5. johnwpowell says:

    You will need to install the extensions on the build machine.  The extensions install a service which is used for packaging and deploying.

  6. Philipp Schmid says:

    Thx for your reply.

    My concern is that IIS needs to be installed and configured on the build machine. Furthermore additional accounts need to be added to the Administrator group to run the service. These are things that are normally not done to a build machine. Is the service required for the packaging build step? Obviously we would not need to deploy on the build machine since its only job is to produce the WSP file.

  7. Chirag Patel says:

    Instead of using VSeWSS 1.3, can I do this with WSPBuilder?

  8. Koray says:

    Thank for the tutorial, it was really helpful.

    but i am facing some problems trying this method. executing the command

    <!– Open a second instance of the dev environment and build using /package switch  –>

           <Exec  WorkingDirectory="$(SolutionRoot)" Command="&quot;$(IDEPath)devenv&quot; &quot;$(BuildDirectory)SourcesMSBuildPartMSBuildPart.sln&quot; /deploy debug /package" />

    from my local machine i get the error "Activex components can not be created"

    if i execute the command on the server-machine via cmd an errordiolog pops up telling me

    "the associated source vontrol plug-in is not installed or could not be initialized. common causes for this error include server unavailability and/or incorrect workspace mappings"

    options are:

    temporarily work uncontrolled

    Permanently remove source control association bindings

    i would appricate a suggestion from you side…

    thanks.