How to get TFS to automate your WSP builds with WSPBuilder

UPDATE: If you want to build WSP files with WSPBuilder and TFS then check out my updated post “Use TFS to automate even better WSP builds with WSPBuilder

Now this is what happens when I have too much time on my hands over the holidays (you would think I would have something better to do, and yet here I am anyway!). Essentially I had rebuilt my laptop and I have included TFS in the setup for my source control on local projects that I was working on (for example, when I’m on site with a client and not connected to the corporate source control solution, this makes for a good backup). Anyway I wanted to look into something that I know has plagued me on other projects, and that was how to use the TFS Build Server to automatically build our WSP files for us as part of its automated builds. So here is how I got that right (and I’ll warn you now, this is gonna be a long blog post!), but the thing is I don’t have a massive amount of experience with TFS outside of the basic stuff, I also am keen to see if anyone out there has ideas on how to improve this process for everyone.

Essentially what I wanted to work was when I do a local build of a project that it gives me a copy of the WSP file that isn’t checked in and is just local to my environment, so I can test it on my local server before checking the code in. Then when I check the code in I want an automated build to take place and drop the WSP file in a location I specify.

Step One: Install WSPBuilder on the build server

So the first thing I decided upon was that I was using WSPBuilder for my WSP files, I have used WSPBuilder for every SharePoint project I have worked on and I like the way it all ties in to Visual Studio, so it seemed that the next step for me was to get WSPBuilder to make my automated build WSP files. To do this, the software needed to be installed on the build server, so that was step 1.

Step Two: add WSPBuilder to the paths environment variable

My next issue was that I wanted to be able to create the WSP files from the build action when I build from the client and when TFS does the builds. This meant that I would put the majority of the actions into the post build activities of each project. This raised an interesting issue for me – my host OS where I do my development is 64-bit and my TFS server (which is the build box) is 32-bit (see my previous rant about TFS not running on 64-bit). This meant that the path to the WSP builder app was going to be different on each box (because of the “program files” and “program files (x86)” thing). So to simplify the post build actions a bit I decided to add the WSPBuilder path (C:Program Files (x86)WSPToolsWSPBuilderExtensions) to the PATH system variable on both the development box and the build box.

Step Three: Set files included in the WSP file to copy to output directory

Now, when TFS does a build in the directory that it puts the DLL’s it will only give you the DLL’s themselves, not the files that also need to go into your WSP. My idea for a way around this is to put all of the files that get deployed to the 12 hive (so everything in my projects that isn’t code) to copy to the output directory on build (this is done by selecting the file in the solution explorer of Visual Studio, and in the properties window set the item to be Content, and to copy if newer). This would mean that my compiled DLL’s and all the other files for the 12 hive would be in the same directory at the same time on the TFS server, ready for the WSP builder call to run.

Step Four: Add the post build activity to the appropriate projects

Now that we know all the files will be where we need them to, we can add the post build actions. Add this to the post build of each project that should output a WSP file:

   1: IF ("$(OutDir)")==("bin\Debug") GOTO LocalBuild
   2:
   3: :TfsBuild
   4:
   5: MD "$(TargetDir)bin\Debug"
   6: COPY "$(TargetDir)*.dll" "$(TargetDir)bin\Debug"
   7: WSPBuilder -SolutionPath "$(TargetDir)" - OutputPath "$(TargetDir)"
   8: RENAME "$(TargetDir)Debug.wsp" "$(ProjectName).wsp"
   9: RMDIR "$(TargetDir)bin" /S /Q
  10: RMDIR "$(TargetDir)12" /S /Q
  11:
  12: GOTO Finish
  13:
  14: :LocalBuild
  15:
  16: WspBuilder -SolutionPath "$(ProjectDir)" -OutputPath "$(TargetDir)"
  17:
  18: :Finish

This code is broken down into the following key actions:

  • Firstly, determine if the build is a local build or a TFS build. Now I’m sure there has got to be a better way to do this, but here is one that works – in a local build the $(OutDir) variable will always be relative (like “bin\debug”) but in a TFS build. this path will always be the complete path to where the build is outputting to, so by checking this we can determine where the build is happening. If it is a local build we use a go to statement
  • The Next section is the TFS build actions. Firstly we create a “bin\debug” folder and move all the DLLs from the solution to it. This essentially makes the “Debug” folder that is the output for the DLLs the equivalent of the solution directory as far as WSP builder is concerned (remember all the other supporting files are still in the 12 folder because we copied them to the output directory). So once the DLL’s are moved we call WSPBuilder (with just the name of the app, not the path to the executable because we added the path to the PATH environment variable earlier). This will always then output a WSP file called “debug.wsp” so we need to rename it to something more useful, so change it to match the ProjectName, and then when we are done delete the bin and 12 directories (we don’t need them dumped to the drop location now that we have the WSP file)
  • The last section is just a call the WSPBuilder that will run for local builds. This does the same thing as if you had called the “Build WSP File” option from within Visual Studio.

So with this post build action in place, when we do a build, either local or on the build server, we will have a new WSP file ready to roll.

Step Five: Create a new build in TFS

This is pretty straight forward, create a new build that targets the debug build of the solution. Nothing too complicated here other than to just make sure you choose the debug option – WSPBuilder expects everything to be in the debug folder.

Step Six: Add additional reference paths

Now unless your TFS server happens to be a MOSS server as well (which may be the case since it requires WSS for it to run) chances are you are referencing DLL’s in your code that the build server won’t know about, which will cause your new build to fail. There are a couple of solutions here, the first is to just GAC what you need, but I personally don’t like this one. The second option that I did is to create a folder on the build server and add it as an additional reference path. This tells the TFS builder to look for referenced DLLs in a specific location. To do this, open the TFSBuild.proj file that is created for your TFS build and add this to the appropriate section (the very last one by default, read the comments to be sure).

   1: <AdditionalReferencePath Include="C:\Assemblies" />

Then you can set this folder up as a network share and drop referenced assemblies in there as required.

Step Seven: Add a pre-build action

Now this isn’t required if your solution only has one project and one WSP file, but if like my example I had set up here locally it has two projects that each have a WSP file then you might need to add this to the pre-build of each project (and I can’t explain exactly why it works yet, I’m tired and was just happy that it all works in the first place).

   1: IF ("$(OutDir)")==("bin\Debug") GOTO Finish
   2:
   3: DEL "$(TargetDir)*.dll" /F /S /Q
   4:
   5: :Finish

If you don’t put this in then you will find that the DLL’s from the first projects that are build will make their way into the second and subsequent WSP files. Put this in and all seemed to work well for me.

So now if you put all of that together and run an automated build (I’ve been testing this with a build on each check-in) you should find that your drop location will now contain all the DLL’s, PDB and config files, as well as your WSP files! Now while this is working for me, there are things I do plan to improve about this process to make it a bit more streamlined, so if you have any suggestions I would love to hear them, just leave me a comment. One thing I have on my to-do list is to automate the upgrade of the solution to a specified server – this would mean that you could automate your WSP file builds nightly and deploy them to a staging server automatically over night. If I do that I’ll be sure to add more to my blog here, and also I do plan on putting this up at http://SharePointDevWiki.com to see what else people can add to it (again, too tired to do it now and my Xbox is calling me to spend some time with it before I go back to work, so it will be there soon). I hope this helps someone else out there!


    • Brian Farnhill
    • December 31st, 2008

    Thanks Merill, I’ll have a look at it and see if I can get it all going like that and post an update when I get it going!

    • Merill Fernando
    • December 31st, 2008

    Step Two: add WSPBuilder to the paths environment variable
    You can skip this step by having the script figure out the program files folder. Here’s on trick from http://stackoverflow.com/questions/346175/use-32bit-program-files-directory-in-msbuild
    <PropertyGroup>
    <ProgramFiles32 Condition="Exists(‘$(PROGRAMFILES) (x86)’)">$(PROGRAMFILES) (x86)</ProgramFiles32>
    <ProgramFiles32 Condition="$(ProgramFiles32) == ”">$(PROGRAMFILES)</ProgramFiles32>
    </PropertyGroup>
    <Exec WorkingDirectory="src\app1" Command=’"$(ProgramFiles32)\doxygen\bin\doxygen" Doxyfile’ />

    Overall, it might be a good idea to create a build file that will be called by msbuild and have all the actions that need to be taken on the TFS (build) server as msbuild tasks in the build file. This is as opposed to using the Visual Studio pre and post build actions which are typically frowned upon by build masters ;)

    Good job. Cheers!

  1. Thanks for your continued support on sharepointdevwiki.com mate. I really appreciate it! I think it’s great that people can collaborate on these ideas further in a wiki engine. I’ve learnt heaps from this experience already!

    • aasof
    • April 20th, 2010

    Hi
    I need to create a deployment kit to deploy wspbuilder plus an executing job to create SQL server database. What is the best practice to do so? Should I use Visual Studio 2008 setup project to call WSPBuilder (to deploy Sharepoint webpart, ect) and also call another executable program that creates SQL server database?
    Cheers

    • That is certainly one way of doing it – what I would do though is write a powershell script to put it all together though. Use PowerShell to call stsadm to deploy your WSP file to the farm, and then use it to call osql to execute your SQL stuff