Office Space

Simplify SharePoint Development with STSDEV

Ted Pattison

Code download available at:OfficeSpace2008_03.exe(762 KB)

Contents

The RootFiles Directory
Automating the Building of Solution Packages
Getting Started with STSDEV
Creating Custom Build Targets with MSBuild
Extending the STSDEV Utility

I started developing for Windows® SharePoint® Services (WSS) 3.0 and Microsoft® Office SharePoint Server (MOSS) 2007 back in the summer of 2005. Since that time I have created hundreds of new class library projects and configured them by hand to develop various types of SharePoint components such as Features, Application Pages, Page Templates, and Web Parts.

After a while I started to standardize on a common project structure of files and folders within the solutions I created with Visual Studio® 2005 for SharePoint development. However, this had a painful aspect: for each project, it would always take me about five or ten minutes just to create the basic structure that would be my starting point. Very little was different in the source files from project to project other than the project name.

Another painstaking issue many developers face with SharePoint development has to do with creating the necessary files to generate the solution package file (.wsp) required for testing and deployment. Like many other SharePoint developers, I grew accustomed to creating and updating a manifest.xml file and a .ddf file by hand in order to generate my solution packages.

Over the past year I have been collaborating with some other SharePoint MVPs including John Holliday, Andrew Connell, and Scot Hillier, who had all come up with very similar yet slightly different schemes to accomplish the same goal. We discussed the idea of designing and implementing a code generator that could automatically produce all the source files needed for a new Visual Studio project and solution. The goal, of course, would be to provide a much faster technique for getting up and running with a new SharePoint development project.

Once we created a code generator that could build every new Visual Studio solution with a common folder structure, it would then also be possible to write the code required to regenerate the manifest.xml file and the .ddf file required to produce the solution package .wsp file. This would eliminate the need to modify manifest.xml or the .ddf file by hand.

This month's column is accompanied by a proof-of-concept application named STSDEV.EXE that demonstrates how to create a simple code generator for SharePoint development. STSDEV is a console application that makes it easy to develop components for MOSS 2007. Furthermore, the fact that it generates code in a repeatable fashion also makes it possible to build Visual Studio solutions for SharePoint development that adhere to best practices.

My goal in this month's column is to discuss how to use the STSDEV utility to create and extend a Visual Studio project that produces an effective and flexible environment for SharePoint development. The approach outlined here provides flexibility because it can be used with either Visual Studio 2005 or Visual Studio 2008. Furthermore, this approach will ease migration when it's time to move from one version of Visual Studio to the next.

The RootFiles Directory

An important aspect of understanding how deployment works in SharePoint development is knowing where your templates files need to be copied on the front-end Web server. A significant percentage of the files must be deployed on each and every front-end Web server inside a location known as the SharePoint RootFiles directory, which in WSS 3.0 is located here:

C:\Program Files\Common Files\Microsoft Shared\web server extensions\12

Take a look at Figure 1 to review the child directories nested inside the RootFiles directory. The directory into which you most commonly need to deploy custom templates files is the TEMPLATE directory. But it is also sometimes necessary to deploy files inside some of these other child directories, such as the ISAPI directory when you are deploying a custom Web service and the Resources directory when you need to deploy .resx files with global resources.

Figure 1 Folders Within RootFiles

Figure 1** Folders Within RootFiles **

The TEMPLATE directory also contains several important child directories. For example, each custom feature requires its own named directory within the FEATURES directory that contains a feature manifest named feature.xml. Application pages should be copied to a child directory within the LAYOUTS directory.

Automating the Building of Solution Packages

It is a best practice in MOSS 2007 development to deploy all custom templates and components using solution packages. In fact, if you ever discover a component type that cannot be deployed via a solution package, you should consider avoiding it when designing and developing SharePoint-based solutions because it will compromise your ability to install, maintain, and upgrade your development efforts in a production or staging environment.

When you are developing for WSS 3.0 or MOSS 2007, you should configure Visual Studio projects to generate a solution package for you, and you should do it early in the development cycle of any project. I have seen many developers defer creating the solution package for testing purposes until late in the development lifecycle, which can cause problems and unexpected delays. It is much better to do the extra work of configuring your Visual Studio project to build the solution package right away and to conduct all testing using solution package deployment.

So how do you configure a Visual Studio project to automate the build of the .wsp file that represents the solution package? You have a few choices. For example, you can write a batch file that calls the MAKECAB.EXE utility to generate the .wsp file and then call it from a build event in Visual Studio. But while this method provides a quick and easy way to automate running commands from the Visual Studio build process, it is more flexible and more maintainable to use custom build targets instead.

The STSDEV utility was designed to create Visual Studio solutions that allow you to run many SharePoint-specific command sequences right from within the Visual Studio development environment. Whenever the STSDEV utility creates a new project, it does so with several different configurations and adds an MSBuild custom build target to each configuration. That makes it possible to simply switch between configurations and then run the standard Visual Studio build command to execute a specific build target. This target contains a common sequence of commands used in SharePoint development, such as using the MAKECAB.EXE utility to build a solution package and then using the STSADM.EXE utility to install and deploy the solution package in the local farm.

Getting Started with STSDEV

Let's start off with a quick walkthrough of how the STSDEV utility works. You can begin by running STSDEV without passing any parameters. The utility brings up a dialog that prompts the user to select the type of SharePoint development solution to be generated (see Figure 2).

Figure 2 Creating a Visual Studio Solution with STSDEV

Figure 2** Creating a Visual Studio Solution with STSDEV **(Click the image for a larger view)

The STSDEV utility was designed with extensibility in mind. Each of the available solution types is basically a solution provider class that implements a very simple STSDEV-specific interface named ISolutionProvider:

public interface ISolutionProvider { string Title { get; } void InitializeSolution(); void AddSolutionItems(); }

The goal behind this design is that advanced SharePoint developers can easily extend STSDEV by creating their own solution providers, which can then be used to generate Visual Studio projects for a particular scenario or to meet the standards and guidelines of a particular company.

However, every solution and project created using STSDEV will always share a common structure and a standard set of deployment files that are used to generate the output solution package. The STSDEV utility also creates each Visual Studio solution with a standard set of configurations and custom build targets that allow developers to automate many of the operations needed during development such as installing, deploying, or retracting the current project's solution package.

To get a better idea, let's examine the structure of a HelloWorld solution generated using the Empty Solution provider. As you can see from Figure 3, STSDEV generates a solution file, named HelloWorld.sln, and a project file, HelloWorld.csproj, that can be created using either the Visual Studio 2005 solution format or the Visual Studio 2008 solution format. Also keep in mind that the STSDEV utility creates Visual Studio projects using the standard Class Library Project type, which makes it easy to upgrade a Visual Studio 2005 project to Visual Studio 2008.

Figure 3 HelloWorld Project Structure

Figure 3** HelloWorld Project Structure **

Using its standard project structure, the STSDEV utility creates a folder named DeploymentFiles and another folder named RootFiles. The DeploymentFiles folder contains four files that are used to build, deploy, and debug the output solution package:

Manifest.xml The solution package manifest required by SharePoint that must be built into the output solution package file (for example, HelloWorld.wsp).

SolutionPackage.ddf A diamond definition file used as input to the MAKECAB.EXE utility to build the solution package file.

SolutionConfig.xml A file used by STSDEV to track additional solution metadata required to regenerate manifest.xml and SolutionPackage.ddf. This metadata includes a GUID for Solution ID, the output solution package name, and optionally additional assembly deployment information, such as SafeControl entries and custom Code Access Security (CAS) settings.

Microsoft.SharePoint.targets A file containing custom build targets in MSBuild format that are used in SharePoint development.

While the DeploymentFiles folder always contains these four files, the contents of the RootFiles folder will vary depending on what type of solution you selected. The RootFiles folder initially contains no files or folders when you create an empty solution. However, the RootFiles folder is where you add each template file that needs to be deployed within the RootFile directory of each front-end Web server.

Each time you add a new file into the RootFiles folder, it will require updates to the manifest.xml file as well as SolutionPackage.ddf. However, the STSDEV utility provides the convenience of updating these files for you each time you rebuild the project. It does so with the RefreshDeploymentFiles command, which regenerates the manifest.xml and SolutionPackage.ddf files by enumerating through the folders and files it finds within the RootFiles directory.

Let's look at an example. When you create an empty solution, the manifest.xml file looks like this:

<Solution SolutionId="24F91DED-8BA7-4633-8BA0-4C9B2A4387D7" ResetWebServer="True" xmlns="http://schemas.microsoft.com/sharepoint/" > </Solution>

Likewise, the SolutionPackage.ddf file looks like this:

; Generated by STSDEV at 1/7/2008 8:42:21 AM .OPTION EXPLICIT .Set CabinetNameTemplate=HelloWorld.wsp .set DiskDirectoryTemplate=CDROM .Set CompressionType=MSZIP .Set UniqueFiles=off .Set Cabinet=on .Set DiskDirectory1=DeploymentFiles DeploymentFiles\manifest.xml

However, what happens if you add some new folders within the RootFiles folder and copy an image file, such as AfricanPith32.gif, into it? The next time you run the Visual Studio build command, there is a custom build target to call into the STSDEV utility and execute the RefreshDeploymentFiles command. The manifest.xml is then updated to look like this:

<Solution SolutionId="24F91DED-8BA7-4633-8BA0-4C9B2A4387D7" ResetWebServer="True" xmlns="http://schemas.microsoft.com/sharepoint/" > <!--TEMPLATE files--> <TemplateFiles> <TemplateFile Location="IMAGES\HelloWorld\AfricanPith32.gif" /> </TemplateFiles> </Solution>

And the SolutionPackage.ddf file is updated to include this:

... ;*** Solution manifest DeploymentFiles\manifest.xml .Set DestinationDir=IMAGES\HelloWorld RootFiles\TEMPLATE\IMAGES\HelloWorld\AfricanPith32.gif

While the RefreshDeploymentFiles command is able to determine what files to add to manifest.xml and SolutionPackage.ddf, there is still a need to track additional metadata about the solution itself and any assemblies that should be deployed inside the solution package. Therefore, STSDEV has been designed to generate an XML metadata file named SolutionConfig.xml and to read the contents of this file any time it regenerates manifest.xml and SolutionPackage.ddf.

The most basic version of a SolutionConfig.xml file looks like the following:

<Solution> <SolutionId>24F91DED-8BA7-4633-8BA0-4C9B2A4387D7</SolutionId> <SolutionName>HelloWorld</SolutionName> <ResetWebServer>True</ResetWebServer> <AssemblyDeployment>False</AssemblyDeployment> </Solution>

As you can see, the SolutionConfig.xml file tracks the GUID with the solution ID, the solution name, and a Boolean value named ResetWebServer that is used add the instruction in manifest.xml that controls whether WSS will run IISRESET on each front-end Web server after the output solution package has been deployed, upgraded, or retracted.

SolutionConfig.xml also contains an element named AssemblyDeployment that is read by the RefreshDeploymentFiles command. This is used to add configuration about one or more of the assemblies that need to be deployed in the output solution package along with the required assembly configuration information.

For example, if you created the HelloWorld project by selecting Empty Solution (C# assembly), then the initial state of the SolutionConfig.xml file would look like the one shown in Figure 4. The binary file and metadata information would then be added into manifest.xml and SolutionPackage.ddf for assembly deployment.

Figure 4 Deploying an Assembly with SolutionConfig.xml

<!-- SolutionConfig.xml file for an Empty Solution with C# assembly --> <Solution> <SolutionId>24F91DED-8BA7-4633-8BA0-4C9B2A4387D7</SolutionId> <SolutionName>HelloWorld</SolutionName> <ResetWebServer>True</ResetWebServer> <AssemblyDeployment>True</AssemblyDeployment> <!--Assembly files--> <Assemblies> <Assembly Location="HelloWorld.dll" DeploymentTarget="GlobalAssemblyCache"> <SafeControls> <SafeControl Assembly="HelloWorld, [full 4-part assembly name]" Namespace="HelloWorld" TypeName="*" Safe="True" /> </SafeControls> </Assembly> </Assemblies> </Solution>

Creating Custom Build Targets with MSBuild

Instead of using batch files, STSDEV leverages the capabilities of MSBuild to create custom build targets that can be integrated into Visual Studio solutions. In particular, STSDEV creates a file named Microsoft.SharePoint.targets in the DeploymentFiles folder that contains custom build targets. This makes it possible to create each new solution with a complete set of commands commonly used in SharePoint development. Figure 5 provides a list of the custom build targets generated for each new project.

Figure 5 Custom Build Targets

Target Description
DebugBuild After building the debug version of the project's output assembly DLL, this command refreshes the solution's deployment files and then builds the output solution package .wsp file using MAKECAB.EXE.
DebugInstall This command installs the solution package .wsp file into the solution package store of the local farm using the STSADM –addsolution operation.
DebugDeploy This command deploys the solution package in the farm using the STSADM –deploysolution operation.
DebugUpgrade This command upgrades the solution package using the STSADM –upgradesolution operation.
DebugQuickCopy This command performs a simply copy command to copy the contents of the solution's RootFiles folder into the WSS RootFiles directory of the local Web server.
DebugRetract This command retracts the solution package from the farm using the STSADM –retractsolution operation.
DebugDelete This command deletes the solution package .wsp file from the solution package store of the local farm using the STSADM –deletesolution operation.
ReleaseBuild After building the release version of the project's output assembly DLL, this command refreshes the solution's deployment files and then builds the output solution package .wsp file using MAKECAB.EXE.

Note that the use of custom build targets brings up security issues. Visual Studio warns you whenever you open a project or solution with custom build targets to protect developers against unexpected attacks. Therefore, when you open a project created with the STSDEV utility for the first time, you should expect to see the security warning.

This is a good time for me to drill into the contents of the Microsoft.SharePoint.targets file. If you open this file, you should see XML formatted in a structure similar to that shown in Figure 6. As you can see, MSBuild requires a top-level Project element. Within the Project element there are child elements such as PropertyGroup and Target.

Figure 6 Microsoft.SharePoint.targets

<?xml version="1.0" encoding="utf-8" ?> <Project DefaultTargets="DebugBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <PackageName>HelloWorld.wsp</PackageName> <PackageFile>HelloWorld.wsp</PackageFile> <WssRootFilesFolder> <!--RootFiles path--> </WssRootFilesFolder> <MAKECAB> <!-- MAKECAB.EXE path--> </MAKECAB> <STSADM> <!-- STSADM.EXE path--> </STSADM> <STSDEV> <!—STSDEV.EXE path--> </STSDEV> </PropertyGroup> <Target Name="DebugBuild"> <!—custom build target 1 --> </Target> <Target Name="DebugInstall" DependsOnTargets="DebugBuild"> <!—custom build target 2 --> </Target> </Project>

Note that the PropertyGroup section at the top of Microsoft.SharePoint.targets is used to track metadata and values like the path and name of files that are needed within individual target commands. You can see that Microsoft.SharePoint.target tracks the file name of the output solution package as well as the fully qualified file path to utilities such as MAKECAB and STSADM. These is also a property that tracks the local file path to STSDEV itself so that command targets are able to call into the STSDEV utility and execute the RefreshDeploymentFiles command.

Now let's look at a few of the build targets. I will start by examining the commands defined inside the build target named DebugBuild, shown in Figure 7.

Figure 7 DebugBuild Build Target

<Target Name="DebugBuild"> <Message Text="Refreshing Deployment Files..." Importance="high" /> <Exec Command="$(STSDEV) /refresh $(TargetName) $(SolutionDir)" ContinueOnError="false" /> <Message Text="Deleting Solution Package File..." Importance="high" /> <Delete Files="$(ProjectDeploymentFilesFolder)\$(PackageFile)" ContinueOnError="true" /> <Message Text="Building Solution Package (Debug Version)" Importance="high" /> <Exec Command="$(MAKECAB) /F $(ProjectDeploymentFilesFolder)\SolutionPackage.ddf /D CabinetNameTemplate=$(PackageFile)" ContinueOnError="false" /> </Target>

As you can see, the first command inside the DebugBuild target is the one that calls into STSDEV and passes the /refresh parameter to run the RefreshDeploymentFiles command. This causes the STSDEV utility to regenerate manifest.xml and SoutionPackage.ddf as the first part of the solution package build process. After that, there is an MSBuild command executed to delete any existing .wsp file and to rebuild the solution package using the MAKECAB.EXE utility. You should also note the use of Message commands, which are used to display informational messages to the developer inside of the Visual Studio output window while the commands are running.

The DebugInstall command has been written to use the addsolution operation supplied by STSADM to install the newly generated solution package .wsp file into the solution package store of the local farm. Also note the use of the DependsOnTargets attribute, which will first run the DebugBuild target commands anytime the DebugInstall target is executed:

<Target Name="DebugInstall" DependsOnTargets="DebugBuild"> <Message Text="Installing Solution..." Importance="high" /> <Exec Command="$(STSADM) -o addsolution -filename $(ProjectDeploymentFilesFolder)\$(PackageFile)" ContinueOnError="true" /> <Exec Command="$(STSADM) -o execadmsvcjobs" /> <Message Text="" Importance="high" /> </Target>

Many of the operations exposed by the STSADM utility are run asynchronously using timer jobs. Therefore, there is another call to STSADM to run the execadmsvcjobs operation. This operation causes the command to pause and wait until the local SharePoint Web server has been able to run all pending timer jobs to completion. The execadmsvcjobs operation is also called in the DebugDeploy target directly after calling the deploysolution operation:

<Target Name="DebugDeploy" DependsOnTargets="DebugInstall"> <Message Text="Deploying Solution..." Importance="high" /> <Exec Command="$(STSADM) -o deploysolution -name $(PackageName) -local -allowGacDeployment -force" /> <Exec Command="$(STSADM) -o execadmsvcjobs" /> <Message Text="" Importance="high" /> </Target>

Now that you have seen the different custom build targets that exist within the Microsoft.SharePoint.targets file generated by the STSDEV utility, you need to know how to execute them from within the Visual Studio environment. It's very simple. You simply select the Visual Studio configuration associated with the desired build target and run the standard Visual Studio build command, as shown in Figure 8.

Figure 8 Executing an STSDEV-Generated Build Target

Figure 8** Executing an STSDEV-Generated Build Target **(Click the image for a larger view)

The MSBuild technique that is being used to wire up these custom build targets requires adding a Target element into the Visual Studio project file (for example, HelloWorld.csproj). This is something that STSDEV does automatically anytime you create a new solution. If you open up one of the .csproj files and look at the bottom, you will see a Target section that looks like Figure 9.

Figure 9 Targets in a Visual Studio Project

<Target Name="AfterBuild"> <CallTarget Targets="DebugBuild" Condition="'$(Configuration)|$(Platform)' == 'DebugBuild|AnyCPU'"/> <CallTarget Targets="DebugInstall" Condition="'$(Configuration)|$(Platform)' == 'DebugInstall|AnyCPU'"/> <CallTarget Targets="DebugDeploy" Condition="'$(Configuration)|$(Platform)' == 'DebugDeploy|AnyCPU'"/> <CallTarget Targets="DebugRedeploy" Condition="'$(Configuration)|$(Platform)' == 'DebugRedeploy|AnyCPU'"/> <CallTarget Targets="DebugUpgrade" Condition="'$(Configuration)|$(Platform)' == 'DebugUpgrade|AnyCPU'"/> <CallTarget Targets="DebugQuickCopy" Condition="'$(Configuration)|$(Platform)' == 'DebugQuickCopy|AnyCPU'"/> <CallTarget Targets="DebugRetract" Condition="'$(Configuration)|$(Platform)' == 'DebugRetract|AnyCPU'"/> <CallTarget Targets="DebugDelete" Condition="'$(Configuration)|$(Platform)' == 'DebugDelete|AnyCPU'"/> <CallTarget Targets="ReleaseBuild" Condition="'$(Configuration)|$(Platform)' == 'ReleaseBuild|AnyCPU'"/> </Target>

You should notice that the Target section has a name of AfterBuild. This is a well-known target name that Visual Studio automatically runs at the completion of every build. This makes it easy for you to add one CallTarget command per build target with a condition to execute the appropriate custom build target depending on the configuration that has been selected.

Extending the STSDEV Utility

The STSDEV utility ships with a handful of sample solution providers. However, the real potential is in extending STSDEV by adding your own solution provider. If you would like to try this, open the source code for the STSDEV project that accompanies this month's column and examine the class implementation for some of the existing solution providers.

If you open up source files such as SimpleFeatureSolutionProvider.cs or WebPartSolutionProvider.cs and walk through the code in the InitializeSolution and AddSolutionItems methods, you can see that it's pretty easy to build a solution provider. There is a class named SolutionBuilder that contains a public static method used to create many of the common files needed in a SharePoint development project. You can also see that some solution providers (such as the one in SimpleFeatureSolutionProvider.cs) interact with the user to prompt for additional configuration items using a modal dialog box created using Windows Forms.

I hope you find the STSDEV utility both educational and useful in your everyday life as a SharePoint developer. This utility is a product of the SharePoint developer community and the latest version of its source code will always be available on the CodePlex Web site at codeplex.com/stsdev. At the STSDEV site on CodePlex you will find other resources such as an introductory set of screencasts to get you up and running quickly with this utility. Please feel free to provide feedback and to post examples of any solution providers that you create in your own SharePoint development efforts.

Send your questions and comments for Ted to mmoffice@microsoft.com.

Ted Pattison is an author, trainer, and SharePoint MVP who lives in Tampa, Florida. His new book is Inside Windows SharePoint Services 3.0. He also delivers advanced SharePoint training to professional developers through his company, Ted Pattison Group (www.TedPattison.net).