Building WSP Solution out of Multiple Visual Studio Projects


>

Introduction
Recently I have been building a reasonable sized SharePoint solution which is using multiple Visual Studio projects. Using the excellent WSPBuilder by Carlos Keutmann, the SharePoint content can be packaged up build individual solution files.
WSP Builder
Ok so as with these things there have been a few hacks to ensure that WSPBuilder does the right thing when building the package. Before I start I should explain how to use WSPBuilder, basically what you do is define the structure of the SharePoint 12 Hive in your project. So you create one folder called 12 and then create sub folders which mimics the SharePoint 12 Hive structure.
So under the 12 folder you would add the appropriate sub folders for your solution :-
TEMPLATESTEMPLATES\LAYOUTS\[YourAppFolder]TEMPLATES\IMAGES\[YourAppFolder]TEMPLATES\FEATURESTEMPLATES\SITETEMPLATES\YOURSITEDEFFOLDER.....
To add assemblies to your solution you create a GAC folder and an 80 folder at the same level as the 12 Folder.
To add an assembly into the GAC put the assembly in the GAC folder. To add an assembly into the web application bin folder put the assembly in the 80 folder.
The Problem
So far so good but when you have multiple projects building multiple assemblies, it is difficult to organise the files so that WSP Builder can build the solution correctly.
This is made worse when you have a project that builds an assembly which is then used by another of the projects.
To explain this if we have two projects, Project A and Project B. Project A builds assembly ProjectA.dll and Project B builds ProjectB.dll, Project B uses Project A.
When Project B is built it relies on the Project A dll and the assembly is copied into Project B’s build directory.
Now if you wanted to package up the solution using WSP Builder, you can run WSP Builder as part of the post build event by running “c:\program files\wspbuilder\wspbuilder.exe”. However if the assemblies, ProjectA.dll and ProjectB.dll need to be in the GAC then we need to build the project and then copy the dlls into the GAC folder.
Thus when the WSP Builder runs it will pickup that the assemlbies need to go into the GAC and build the wsp solution correctly.
These post build events become more and more complex as the number of projects is included in the solution, more complexity equals more room for errors.
A Solution
In order to make things less complex I use the post build events for the projects to copy all the appropriate content from the project directory and build a directory structure under the solution dir.
This can be done as follows:-
echo Running WSP Builderecho Copying GAC Dlls to $(ProjectDir)GACcopy /y "$(TargetDir)*.dll" "$(ProjectDir)GAC\"

echo Copying 12 Directory to Solution Buildxcopy /S /E /I /H /Y "$(ProjectDir)12" "$(SolutionDir)Build\12\"

echo Copying GAC Directory to Solution Buildxcopy /S /E /I /H /Y "$(ProjectDir)GAC" "$(SolutionDir)Build\GAC\"

echo Running WSP Builder against FCT System Solutioncd "$(SolutionDir)Build\%programfiles%\WSPTools\WSPBuilderExtensions\wspbuilder" -WSPName [solutionfilename.wsp]-SolutionId [SolutionIDGUID]

echo Copying packages to Deployment\Packagecopy /y "$(SolutionDir)Build\*.wsp" "$(SolutionDir)Deployment\Package\" copy /y "$(ProjectDir)Package\*.*" "$(SolutionDir)Deployment\Package\":exit
Some enhancements
One of the annoying things when you are coding is that there is a cycle of fix, build, deploy, debug. If you are building the WSP file each time then the cycle between build->deploy->debug can be quite slow. To speed things up I use different build configurations. For example the Debug build does not call the wsp builder command but uses gacutil to add the assembly to the GAC or copies the assembly to the web application bin directory. The application pool is then recycled.
echo Copying content into $(SolutionDir)
copy /y /e /s/i “$(ProjectDir)\12” “$(SolutionDir)\Build\12”
For example:-
This is achieved in the following manner:-
I hope that you find this useful, I would love to hear about other ways that people achieve this and also if anyone has any enhancements to this solution.

Error when creating custom list from New.aspx


>

Introduction

Anyone who’s been developing SharePoint will I am sure have had problems building custom list schemas. However, this one I hadn’t seen before.

The Problem

A custom site definition had been built which activated a feature which created an instance of a custom list for holding event list items.

When the site was created the events list was created perfectly, no problems.

However, when the list is created through the GUI, by clicking on Site Actions->View All Site Content and clicking Create and choosing the custom list type.

This brings up the new.aspx form, once the title and description is filled out and the create button clicked then the following exception was thrown :-

Exception occurred. (Exception from HRESULT: 0x80020009 (DISP_E_EXCEPTION)) at Microsoft.SharePoint.Library.SPRequestInternalClass.CreateListFromFormPost(String bstrUrl, String& pbstrGuid, String& pbstrNextUrl)
at Microsoft.SharePoint.Library.SPRequest.CreateListFromFormPost(String bstrUrl, String& pbstrGuid, String& pbstrNextUrl)

Solution

So the problem is something to do with the list schema.xml file. This schema.xml file is found in the feature directory. I use the following directory structure:-

12HIVE\Templates\Features\ListFeatureName\ListName\schema.xml

Looking at the error, the stack trace mentions the function, CreateListFromFormPost. I thought the issue was due to a custom NewForm page that we were using.

I could not find anything that would fix the issue, even replacing all the custom xml relating to the NewForm element.

However, the schema.xml was missing an attribute, this was the Url=”” attribute part of the <List> element.

This issue was found by comparing the schema.xml file with an out of the box schema.xml. As soon as the Url attribute was provided (even if the attribute was blank) then the list was created properly.

To recap the section of the schema that needed to updated was:-

<List xmlns:ows=”Microsoft SharePoint” Title=”Events” DisableAttachments=”TRUE” FolderCreation=”FALSE” Url=”Lists/Events” EnableContentTypes=”TRUE” BaseType=”0″>

The way that a list is created through a List Feature Instance must be called by some separate code function probably something like CreateListFromListInstanceFeature() instead of CreateListFromFormPost() and hence the list is created ok by the List Feature.

There are a few conversations about this on MSDN but when I first read them I didn’t realise the solution that they were pointing to.

These posts are:-
http://social.msdn.microsoft.com/Forums/en/sharepointcustomization/thread/458e5716-4fa4-425c-9fda-88ce4ccc6203

and

http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/86dd7485-48cc-4836-ad09-40192bd54841

Anyway hope that helps.

Simon