We use the SharePoint Visual Studio Project Template on CodePlex to create WSP deployment packages for our SharePoint features. I tend to think of this WSP creation project in the same way as a MSI installer; so we don’t put SharePoint components into the WSP solution itself, it is an extra project in the solution that assembles the components from a variety of other solutions (e.g. web parts, workflows, event receivers, shared libraries for the GAC etc) and builds a single deployable WSP file.

Running locally on a developers PC inside Visual Studio this template has worked well, the only change I make from the default is to alter the WSP projects pre-build event script to xcopy all the files into the correct directories to allow the VBScript files to create the WSP.

In our drive to automation and automatic testing I have been looking at getting the WSP created as part of our TFS Team Build process. It turns out you get a few problems because Visual Studio and Team Build do macro expansion differently.

So my Pre-build event becomes

echo PREBUILD STARTED  rem Check if we running in VS or Teambuild if not exist "......CLIENTLIBRARYSharedLibProjectbin$(ConfigurationName)SharedLibProject.dll" goto tfsbuild  echo Copy from VS locations, in this sample we assume a shared library, a webpart and some javascript xcopy "......CLIENTLIBRARYSharedLibProjectbin$(ConfigurationName)SharedLibProject.dll"  "$(ProjectDir)DLLSGAC"  /F /R /Y xcopy "......Web Partbin$(ConfigurationName)\*.dll"  "$(ProjectDir)DLLSGAC"  /F /R /Y xcopy "$(SolutionDir)HOSTbinHOST.dll"  "$(ProjectDir)DLLSGAC"  /F /R /Y xcopy "$(SolutionDir)HOSTjson\*"  "$(ProjectDir)TEMPLATELAYOUTS"  /F /R /Y xcopy "$(SolutionDir)HOST\*.js"  "$(ProjectDir)TEMPLATELAYOUTS"  /F /R /Y  goto end  :tfsbuild echo Copy from TFS build locations  xcopy "$(outdir)SharedLibproject.dll"  "$(ProjectDir)DLLSGAC"  /F /R /Y  xcopy "$(outdir)WebPart.Core.dll"  "$(ProjectDir)DLLSGAC"   /F /R /Y xcopy "$(outdir)WebPart.UI.dll"  "$(ProjectDir)DLLSGAC"   /F /R /Y xcopy "$(outdir)Host.dll"  "$(ProjectDir)DLLSGAC"   /F /R /Y xcopy "$(SolutionDir)HOSTjson\*"  "$(ProjectDir)TEMPLATELAYOUTSjson\*"  /F /R /Y xcopy "$(SolutionDir)HOST\*.js"  "$(ProjectDir)TEMPLATELAYOUTS\*.js"   /F /R /Y  :end  echo PREBUILD COMPLETE

Key points to note here are

  • For Visual Studio you can use Xcopy /s it makes no difference as there are no sub-directories (so you might ask why use it it all, I guess in some cases a generic copy all is easier than specifying a fixed file and directory). This is not the case for Team Build, if you use /s you can get multiple copies of DLLs in sub-directories created. This is because of the way Team Build structures it’s directories. The $(outdir)  is not a subdirectory of the $(solutiondir) as it is in Visual Studio, it is an absolute path defined for the build agents settings where all the outputs for all the projects in the build are assembled. So, depending on the project type, you seem to get sub directories. So it is best to be very specific as to what to copy, avoid wildcards and recursion.
  • When doing a wildcard xcopy as with json* files on Team Build you must specify the copy to file name i.e. json*, if you don’t you get the question ‘is the target a file or a directory’ message which obviously kills the build. This does not occur within Visual Studio.

It is also worth altering the post build event, by default the WSP is created in the project root, but if it is copied to the $(outdir) it ends up in the Team build drop location, so can be picked up by anyone, just like a DLL.

echo POSTBUILD STARTED rem commented out as the build box does not have SharePoint installed rem this could be wrappered in the chheck to see if the directory is present if we are on tema build or not rem XCOPY "$(ProjectDir)TEMPLATE\*" "C:Program FilesCommon FilesMicrosoft Sharedweb server extensions12TEMPLATE" /S /F /R /Y  echo Run the VBscripts to create the XML files "$(ProjectDir)CreateManifest.vbs" "$(ProjectDir)" "$(ProjectName)" "$(ProjectDir)CreateCabDDF.vbs" "$(ProjectDir)" "$(ProjectName)"  echo Build the WSP cd "$(ProjectDir)" makecab.exe /F cab.ddf  echo Copy it to the out directory xcopy \*.wsp "$(TargetDir)\*.wsp" /y  echo POSTBUILD COMPLETE

However your problems do not end here. If you build this WSP project locally on a development PC all is fine. However (depending upon you project) it may fail on Team Build, well actually not fail just pause forever. This due to the way that Team Build checks out folders. The WSP project has a folder structure you drop files in that the VBScript files scan to create the manifest and then the WSP. If one of these directories is empty then it is not created on the build box and the VBScript stalls.

The solution is simply just add an extra folder exists check in the CreateCabDDF.vbs file’s EnumFolder method

sub EnumFolder(sFolder, sRelativePath)     dim oFolder, oFolders, oSub, oFile          rem this is the extra line     If oFS.FolderExists(sFolder) Then          set oFolder = oFS.GetFolder(sFolder)              if (sRelativePath = "TEMPLATE") then sRelativePath = ""         if (sRelativePath = "FEATURES") then sRelativePath = ""              if (sRelativePath <> "") then sRelativePath = sRelativePath + ""              for each oFile in oFolder.Files             oDDF.WriteLine """" + oFile.Path + """" + vbTab + """" + sRelativePath + oFile.Name + """"         next          if (sRelativePath <> "" and InStr(1, sFolder, "FEATURES") > 0) then             sRelativePath = "FEATURES" + sRelativePath         end if         for each oSub in oFolder.SubFolders             EnumFolder oSub.Path, sRelativePath + oSub.Name        next            end if      end sub

Once this is all one the you can build the project in the Team Build