STSDEV – tips and tricks for WSP deployment

For those of you who have given up using any versions of Visual Studio extension for Windows SharePoint Services 3.0
(a.k.a. VSeWSS 1.01.11.2)

                                 GOOD ON YA!!!

– You’ve just saved yourself loads of pain.

(I think this itself deserves a good post, my colleagues are currently building the next version of VSeWSS at the time of this post, may God be with them!)

For those of you who have taken up WSPBuilder or STSDEV,

                              GOOD ON YA!!! (X2)

– SharePoint development have just become less painful :)

Here are a few tips and tricks I learnt along the way and from my experienced and knowledgeable colleagues from Intergen.

#1. Why STSDEV is ignoring changes made in targets file?

Close your solution in Visual Studio and reopen it, and yes we’ll have to repeat this each time we make a change to the targets file. I know it’s a pain in the bum.

What is up with this close/reopen business and how to fix it? When you find out, please dolet me know :)

#2. Why is my solution deployed globally?

image

After a project is created with STSDEV, the default solution deployment command under DebugDeploy Target in targets file is;

<Exec Command="$(STSADM) -o deploysolution -name $(PackageName) -immediate" />

To change your deployment to a site collection;

<Exec Command="$(STSADM) -o deploysolution -name $(PackageName) -immediate -allowgacdeployment -url $(TargetUrl)  -force" />

where $(TargetUrl) is defined as a property between <PropertyGroup> tags at the beginning of the targets file;

<PropertyGroup>
  <PackageName>Play.wsp</PackageName>
  <PackageFile>Play.wsp</PackageFile>
  <TargetUrl>http://intranet/</TargetUrl>
  <!-- Other properties omitted -->
</PropertyGroup>

#3. “This solution contains no resources scoped for a Web application and cannot be deployed to a particular Web application.”

This is a tricky one. The error message is very generic and can mean many things… To resolve it, you need to flick between the targets file and the SolutionConfig.xml file, make changes and trying deploying it.

image

Remember, each time you make a change to the targets file, close your solution and reopen it for it to take effect in the build.

If you’ve written any custom code in the project, like event receivers, and you need deploy this assembly to GAC. You’ll have to make each and very single following points are addressed.

(Depend on the option combinations you chose when you created the solution with STSDEV, none / some / all of these should have been done for you.)

a. The assembly is signed with a key file. (Project -> properties)

image

b. In SolutionConfig.xml, allow GAC deployment and safe control.

<AssemblyDeployment>True</AssemblyDeployment>
<SafeControlSettings>True</SafeControlSettings>

c. In SolutionConfig.xml, specify the safe control.

<Assemblies>
    <Assembly Location="Play.dll" DeploymentTarget="GlobalAssemblyCache">
        <SafeControls>
            <SafeControl Assembly="Play, Version=0.0.0.0, Culture=neutral, PublicKeyToken=abbe9e18e07720bf" Namespace="Play" TypeName="*" Safe="True" />
        </SafeControls>
    </Assembly>
</Assemblies>

You can get the assembly name by opening up the dll in reflector after signing it with a key and compiled the project.

image

d. In targets file, under BuildDeploy target, solution deploy command allows GAC deployment.

<Exec Command="$(STSADM) -o deploysolution -name $(PackageName) -immediate -allowgacdeployment -url $(TargetUrl) -force" />

e. If solution is to deploy to a site collection, specify the URL in the deploy command as well, so it knows which web config to place the safe control in. (See the above code snippet.)

f. If you are intending to deploy to all Site Collections in this Web Application, make sure you specify so by having a -allcontenturl switch in the command.

<Exec Command="$(STSADM) -o deploysolution -name $(PackageName) -immediate -allowgacdeployment -allcontenturls -force" />

Failing all above, keep Live.com ‘in ;)

#4. “This solution contains resources scoped for a Web application and must be retracted from one or more Web applications.”

If you have successfully deployed your wsp solution to a specific site collection following the steps in #2 and 3, then you might come across this message when you try to build the project in “DebugRetract” or “DebugDelete” build targets.

If you take a look at the output windows it fails at line:

"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\bin\stsadm.exe" -o retractsolution -name Play.wsp -immediate

This happens because we haven’t specified the site collection URL that we had deployed the wsp solution to. So, if we change the line to.

"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\bin\stsadm.exe" -o retractsolution -name Play.wsp -immediate -url $(TargetUrl)

Remember to close and reopen your solution for this change to take effect, then build it. You should see the problem go away.

#5. Can’t Debug I hear you say?

There are a couple of things you need to do here,

One is, allows a pdb file to be generated each time you compile the project. in Visual Studio 2008, right click on the project -> Properties -> Build -> Advanced… -> Debug Info -> full

image

So repeat this for all the concerned Configuration target. I recommend do it for “DebugRefreshAssemblyInGac, DebugBuild, DebugDeploy, and DebugRedeploy”. Not sure about “DebugUpgrade” never used it before.

image

Two is adding the pdb files to the correct GAC location. First you’ll need to find out where in GAC you’ll want to copy the pdb files into.

Start -> Run and type in “C:\windows\assembly\GAC_MSIL” as it can’t be opened through a file explorer.

image

Follow your nose and you should find the directory where the bit was deployed to, copy out the path from Address bar, this is the folder you want to deploy your pdb file to.

image

So in your target file, you need add two lines to your “DebugDeploy” target and “DebugRefreshAssemblyInGac” target like to following;

<Target Name="DebugDeploy" DependsOnTargets="DebugInstall">
  <Message Text="Deploying Solution..." Importance="high" />
  <Exec Command="$(STSADM) -o deploysolution -name $(PackageName) -immediate -allowgacdeployment -url $(TargetUrl) -force" />
  <Exec Command="$(STSADM) -o execadmsvcjobs" />
  <Copy SourceFiles="$(TargetDir)$(TargetName).pdb" DestinationFolder="C:\windows\assembly\GAC_MSIL\Play.0.0.0__abbe9e18e07720bf" SkipUnchangedFiles="" />
  <Message Text="$(TargetDir)$(TargetName).pdb copied to GAC for debugging." Importance="high" />
  <Exec Command="$(POSTBUILD)" />
  <Message Text="" Importance="high" />
</Target>

<Target Name="DebugRefreshAssemblyInGac" >
  <Message Text="(Re)installing assembly in GAC and recycling app pool" Importance="high" />
  <Exec Command="$(GACUTIL) -if $(TargetPath)" />
  <Copy SourceFiles="$(TargetDir)$(TargetName).pdb" DestinationFolder="C:\windows\assembly\GAC_MSIL\Play.0.0.0__abbe9e18e07720bf" SkipUnchangedFiles="" />
  <Message Text="$(TargetDir)$(TargetName).pdb copied to GAC for debugging." Importance="high" />
  <Exec Command='$(ISSAPP_SCRIPT) /a "SharePoint - 80" /r' />
  <Message Text="" Importance="high" />
</Target>

Remember to close and reopen your solution each time you change the target file.

image

Make sure the timestamp for these two files are identical, or your code is likely to be out of sync with the pdb.

#6. Have a post build script depending on your build targets?

In your target file, add a new property inside <PropertyGroup> tag.

<POSTBUILD>"$(ProjectDir)PostBuild.bat"</POSTBUILD>

And use this as a command in any targets in your target file, like so;

<Exec Command="$(POSTBUILD)" />

– I hear you ask, why wouldn’t you just use Build Event from project properties?
Because STSDEV introduces a handful of build targets (10 of them) and sometimes it’s useful to have control over which targets you want to have a post build script while not others. Also, we can run different post build scripts for different build targets leveraging this approach, just define different scripts as properties inside <PropertyGroup> tag then just invoke the appropriate ones from different build targets.

– Note, in theory we should be able to put anything we have in a post build script in the target file. After all, the targets file is just a script file in an XML format. But to me, I want to be able to share my build script around with other developers or from project to project. By extracting some of the common tasks out to a build script I’m protecting myself against having to relying on STSDEV. After all, STSDEV is just one of the great tools out there. That’s just my opinion, your mileage may vary.

#7. Have multiple projects under one solution?

When you first create a project with STSDEV and call it Play, It will lay out its folder structure like so;

image

When you create more than one projects and shift them to a sub directory to where the solution file resides, like so;

image

Then you compile your project, you’ll see this error;

image

and this in your Output Window;

STSDEV - Simple Tools for SharePoint Developers
Refreshing Solution Package Files
Current Solution Name:
Current Solution Directory:
Refreshing deployment files...

Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Dev\Play\Play\DeploymentFiles\SolutionConfig.xml'.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamWriter.CreateFile(String path, Boolean append)
   at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize)
   at System.IO.StreamWriter..ctor(String path)
   at stsdev.SolutionConfigBuilder.Create(String FilePath, Boolean AssemblyDeployment, Boolean SafeControlSettings, AssemblyDeploymentTarget DeploymentTarget, PermissionSet CasPermissions)
   at stsdev.SolutionConfigBuilder.Create(String FilePath, Boolean AssemblyDeployment, Boolean SafeControlSettings, AssemblyDeploymentTarget DeploymentTarget)
   at stsdev.SolutionConfigBuilder.Create(String FilePath, Boolean AssemblyDeployment, Boolean SafeControlSettings)
   at stsdev.SolutionConfigBuilder.Create(String FilePath, Boolean AssemblyDeployment)
   at stsdev.SolutionBuilder.LoadSolutionConfigFile()
   at stsdev.SolutionBuilder.RefreshDeploymentFiles()
   at stsdev.SolutionBuilder.RefreshDeploymentFiles(String TargetSolutionName, String TargetSolutionDirectory)
   at stsdev.Program.Main(String[] args)
MSBUILD : warning MSB3073: The command ""C:\Program Files\STSDev v1.3\stsdev.exe" /refresh "Play" "C:\Dev\Play\Play\"" exited with code -532459699.

This happens because, STSDEV uses solution directory to reference its deployment files and these files are per-project based. When we moved the project related files and folders else where, we’ll need to reference them by project directory.

All you need to do is to change the follow line in your <PropertyGroup> in your targets file.

<REFRESH>$(STSDEV) /refresh "$(TargetName)" "$(SolutionDir)"</REFRESH>

to

<REFRESH>$(STSDEV) /refresh "$(TargetName)" "$(ProjectDir)"</REFRESH>

Close and reopen the solution then try building the project again!

#8: “error : Cannot create INF file: setup.inf”

This happens because STSDEV will try to create/write to files called setup.inf andsetup.rpt. The chances are you have these two files under source control so they are locked.

setup.infsetup.rpt in project directory, along with manifest.xml andSolutionPackage.ddf in DeploymentFiles folder will get regenerated and written to each time you build your project. So don’t have them under source control.

image

#9: “Where are my web stuff?”

Enlightened by a colleague of mine – Matt Smith – a SharePoint Evangelist. When you create a project with STSDEV, the project is a C# library type.

image

When we add New Items, there isn’t a great of items are available to us. Especially when we develop SharePoint solutions, ASP.NET artifacts like ascx User Controls, aspx pages etc…

image

For us to be able to add web stuff to the project, Visual Studio needs to see the project we are adding stuff to – is a Web Application Project. To achieve this, add the following line of xml to the first <PropertyGroup> node of your .csproj file.

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

So,

<PropertyGroup>
  <Configuration Condition=" '$(Configuration)' == '' ">DebugBuild</Configuration>
  <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
  <ProductVersion>9.0.21022</ProductVersion>
  <SchemaVersion>2.0</SchemaVersion>
  <ProjectGuid>{3C135253-DDB3-421F-B27A-FC0481335E58}</ProjectGuid>
  <OutputType>Library</OutputType>
  <RootNamespace>Play</RootNamespace>
  <TargetFrameworkVersion>v3.0</TargetFrameworkVersion>
  <SignAssembly>true</SignAssembly>
  <AssemblyOriginatorKeyFile>KeyFile.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

becomes

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <Configuration Condition=" '$(Configuration)' == '' ">DebugBuild</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.21022</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{3C135253-DDB3-421F-B27A-FC0481335E58}</ProjectGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>Play</RootNamespace>
    <TargetFrameworkVersion>v3.0</TargetFrameworkVersion>
    <SignAssembly>true</SignAssembly>
    <AssemblyOriginatorKeyFile>KeyFile.snk</AssemblyOriginatorKeyFile>
  </PropertyGroup>

Reload the project and we see the project icon has become it of a Web Application Project;

image

and we can add all the web stuff to our project now;

image

There you go, the joys and pains for working with STSDEV.

Leave a comment