Following a comment left elsewhere on this blog, I thought I'd cover exactly what SharePoint Solution packages are, including how they are built and deployed to other SharePoint environments.
First of all, let's be clear on exactly what a Solution package is. Note I'm using 'Solution' with a capital S in this article to distinguish between what we are discussing here and the general idea of a technical solution built using SharePoint. A Solution is a package of SharePoint items for deployment - in physical terms it is a cabinet file (.cab) which is given the extension of .wsp to differentiate it from standard .cab files. Other articles on this blog cover the idea of using SharePoint Features to deploy functionality, so let's also be clear on the relationship between Features and Solutions. In general terms, some of the tasks which cannot be done with a Feature alone but can be done with a Solution include:
- Deployment of certain files to the filesystem, e.g. an assembly for workflow or web parts, custom files which will reside in the ‘12’ folder
- Deployment of web part definition files (.webpart)
- Web.config modifications e.g. the ‘SafeControls’ entry required for a custom web part
- Code Access Security config modifications e.g. those required for custom web parts not running from the GAC
In addition to being able to do these things, Solutions can also contain Features. The way I think of it is that the Solution wraps the Feature. In actual fact, I always recommend that even if they don't need to be (e.g. we're not doing anything in the list above), Features are always deployed as part of a Solution. The reason for this is that the Solution framework takes care of deploying all required files to all Web Front End (WFE) servers in a SharePoint farm. This alone is incredibly useful in working towards repeatable deployments, and ensures your WFEs stay synchronized.
How Solutions are specified
The key file used to specify what a Solution package consists of is the manifest.xml file. Going back to my earlier post on deploying web parts, the manifest.xml file for that scenario looks like this:
<Solution xmlns="http://schemas.microsoft.com/sharepoint/" SolutionId="122C0F04-78B7-4d42-9378-6F8B4F93ADD1">
<FeatureManifests>
<!-- note this is the location in the cab file! -->
<FeatureManifest Location="COB.Demo.WebPartDeployment.WriteToFileWebPart\feature.xml" />
</FeatureManifests>
<Assemblies>
<Assembly Location="COB.Demo.WebPartDeployment.WriteToFileWebPart\COB.Demo.WebPartDeployment.WriteToFileWebPart.dll"
DeploymentTarget="GlobalAssemblyCache">
<SafeControls>
<SafeControl Assembly="COB.Demo.WebPartDeployment.WriteToFileWebPart, COB.Demo.WebPartDeployment, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5"
Namespace="COB.Demo.WebPartDeployment"
Safe="True"
TypeName="*" />
</SafeControls>
</Assembly>
</Assemblies>
<DwpFiles>
<DwpFile
Location="COB.Demo.WebPartDeployment.WriteToFileWebPart\COB.Demo.WebPartDeployment.WriteToFileWebPart.webpart"
FileName="COB.Demo.WebPartDeployment.WriteToFileWebPart.webpart" />
</DwpFiles>
</Solution>
This is assuming the web part is being deployed to the GAC - for this illustration this is a simpler scenario than deploying to the web application's bin directory, where Code Access Security (CAS) policy would also be required. Effectively, the manifest specifies that the Solution package consists of the following:
- a Feature with a header file named 'feature.xml'
- an assembly for deployment to the GAC named 'COB.Demo.WebPartDeployment.WriteToFileWebPart.dll'
- an entry in the SafeControls section of the application's web.config file, specifying all types in the specified assembly should be treated as safe
- a web part definition file named 'COB.Demo.WebPartDeployment.WriteToFileWebPart.webpart' - this will be deployed to the web part gallery on the site
Importantly, all these details in the manifest.xml file are only used by SharePoint when the generated Solution package is deployed. They are effectively the instructions which say "go and get this item from the .wsp file and put it here". The actual process for generating the .wsp file is another task.
Building Solution packages
There are 2 options for building the actual package - build it manually using makecab.exe, or use an automated solution - there are several community-developed tools/techniques available. Since it's always good practise to understand what's actually happening in such processes, we'll cover how to do it manually here.
The first step is to write a .ddf (Diamond Directive File). This is a set of instructions for makecab.exe on how to build the folder hierarchy inside the .cab file. For my web part example, my file looks like:
.OPTION Explicit
.Set CabinetNameTemplate="COB.Demo.WebPartDeployment.WriteToFileWebPart.wsp"
.Set DiskDirectory1="Package"
;***
manifest.xml
;** this directory name is used for the folder name under 12\TEMPLATE\Features, so should
;** match up with what you want to call the feature!
.Set DestinationDir=COB.Demo.WebPartDeployment.WriteToFileWebPart
elements.xml
feature.xml
.Set DestinationDir=COB.Demo.WebPartDeployment.WriteToFileWebPart
WebPart\COB.Demo.WebPartDeployment.WriteToFileWebPart.webpart
WebPart\COB.Demo.WebPartDeployment.WriteToFileWebPart.dll
;***
This file tells makecab.exe to do the following:
- create a .cab file named 'COB.Demo.WebPartDeployment.WriteToFileWebPart.wsp'
- put the 'manfest.xml' file at the root of the hierarchy
- put the 'elements.xml' and 'feature.xml' files in a subfolder called 'COB.Demo.WebPartDeployment.WriteToFileWebPart'
- also put the 2 other files (with extensions .webpart and .dll) in this same subfolder, but that makecab.exe should look for these files in a subfolder on the main filesystem called 'WebPart'
This instructions file is then passed to makecab.exe with the following command-line command:
D:\SolutionDeployment\Development\COB.Demo.WebPartDeployment>"C:\Program Files\Microsoft Cabinet SDK\BIN\MAKECAB.EXE" /f COB.Demo.WebPartDeployment.ddf
Couple of things to note here. At the command prompt we have ensured the current directory is the directory where all our Solution files are kept. This ensures that our relative references in the .ddf file above can be resolved. We pass the /f parameter to indicate we are passing a directives file, and also pass the location of this file.
Assuming all the files/references are OK, this results in a .cab file with a .wsp extension which will be created in a subfolder under the current directory called 'Package' (the folder will be created for you if it doesn't exist). We now have a Solution package which can be deployed to another SharePoint server. But first let's take a peek inside - this can be done by temporarily renaming the extension to .cab. You should then see something like:
Deploying the Solution to other SharePoint servers
The final part is to actually deploy the Solution. First the .wsp file must be copied to the target server, and then we will use STSADM (SharePoint's command-line admin tool) to actually deploy the Solution. The following are the commands which used to perform the deployment:
stsadm -o addsolution -filename COB.Demo.WebPartDeployment.WriteToFileWebPart.wsp
stsadm -o deploysolution -name COB.Demo.WebPartDeployment.WriteToFileWebPart.wsp
-url http://myWebApplication -immediate -allowGacDeployment -allowCasPolicies -force
The first command simply adds the Solution package to SharePoint's Solution store in the config database, and the second one actually performs the deployment of the package to the specified web application. These areas are fairly well covered in the SDK at http://msdn2.microsoft.com/en-us/library/aa544500.aspx.
Rather than write these commands at the command-line each time you wish to deploy the package, chances are over time you'll package these commands up into STSADM scripts which do the work. As an example, the script I use for the webparts example looks like:
:begin
@echo off
set solutionName=COB.Demo.WebPartDeployment.WriteToFileWebPart
set url=http://myWebApplication
set featureName=COB.Demo.WebPartDeployment.WriteToFileWebPart
@set PATH=C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN;%PATH%
echo --- Attempting to deactivate/retract existing solution...
stsadm -o deactivatefeature -name %featureName% -url %url% -force
stsadm -o retractsolution -name %solutionName%.wsp -url %url% -immediate
stsadm -o execadmsvcjobs
stsadm -o deletesolution -name %solutionName%.wsp -override
rem stsadm -o execadmsvcjobs
echo --- Adding solution %solutionName% to solution store...
stsadm -o addsolution -filename %solutionName%.wsp
if errorlevel == 0 goto :deploySolution
echo ### Error adding solution %solutionName%
echo .
goto end
:deploySolution
echo --- Deploying solution %solutionName%...
stsadm -o deploysolution -name %solutionName%.wsp -url %url% -immediate -allowGacDeployment -allowCasPolicies -force
stsadm -o execadmsvcjobs
if errorlevel == 0 goto :activateFeature
echo ### Error deploying solution %solutionName%
echo .
goto end
:activateFeature
echo --- Activating features in solution %solutionName%...
stsadm -o activatefeature -name %featureName% -url %url% -force
if errorlevel == 0 goto :success
echo ### Error activating feature %featureName%
echo .
goto end
:success
echo Successfully deployed solution and activated feature(s)..
echo .
goto end
:end
pause
Following the script through, this basically does the work of safely upgrading the Solution (in case it has been deployed previously), and also activating the Feature within the Solution.
Automating Solution builds
Earlier, we mentioned the idea of automating the process of building and deploying Solutions. There are several community tools/ideas in this area, a couple of the ones I'm aware of are:
Both of these effectively use Visual Studio post-build events to automate the build of the .wsp package.
Additionally, Microsoft's Visual Studio Extensions for Windows SharePoint Services effectively performs the same tasks. Although there is some inflexibility in the current version, this add-on for VS means that the whole process is automated by hitting the F5 button. I'd recommend taking a look if you're not aware of this. My post on creating lists with VSeWSS gives an overview of using it for that specific scenario.