Tuesday, July 29, 2014

SharePoint: exclude files from WSP

copyright from: shareddev.blogspot.com

Publishing of WSPs in Visual Studio is very simple and straightforward. But this process includes absolutely everything into WSP (aspx, ascx, css, js, etc), even if we don’t need them. For example, if we have minified versions of javascript files, we definitely don’t need debug versions in production. SharePoint tooling doesn’t really help us when we need to customize building process for our solutions. But luckily we have MsBuild! I’ll come up with simple solution on how it’s possible to exclude some files from Layouts directory. Everything that will be described below has been tested in Visual Studio 2012 and SharePoint 2010.

When we click Publish button from VS menu, it executes the following MSBuild file:

c:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets

A closer look to this file shows that we can officially override two targets here: BeforeLayout and AfterLayout. We need to do two things - exclude file from WSP and exclude it from manifest.xml. Excluding from WSP is simple enough. According to Microsoft.VisualStudio.SharePoint.targets file there is EnumeratedFiles ItemGroup created before BeforeLayout target, so we can exclude some files in this target like that:

12345678
Name="BeforeLayout" Condition="'$(Configuration)'=='Release'">
Files="@(ExcludedFiles)" SourceFiles="@(EnumeratedFiles)">
ItemName="Result" TaskParameter="Result" />
Remove="@(Result)"/>
view rawgistfile1.xml hosted with ❤ by GitHub
ExcludedFiles ItemGroup should be defined earlier. FindInEnumeration is simple task that finds files in MSBuild ItemGroup array. Using MSBuild 4.0 we can use little C# snippets in order to create tasks:

12345678910111213141516171819202122232425262728293031323334353637383940414243
TaskName="FindInEnumeration" TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
Include="System" />
Include="System.Core" />
Include="System.Xml" />
Include="System.Xml.Linq" />
Include="Microsoft.CSharp" />
Namespace="System" />
Namespace="System.Reflection" />
Namespace="System.IO" />
Namespace="System.Linq" />
Namespace="System.Xml.Linq" />
Namespace="System.Net" />
Namespace="Microsoft.Build.Framework" />
Namespace="Microsoft.Build.Utilities" />
 
Type="Fragment" Language="cs">
<![CDATA[
try
{
var filesToFind = Files.Select(item => item.GetMetadata("FullPath")).ToArray();
 
Result = SourceFiles
.Where(file => filesToFind.Contains(file.ItemSpec))
.ToArray();
return true;
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
return false;
}
]]>
After we excluded items from WSP let’s exclude them from manifest.xml. We have to do it separately because manifest files are gathered before BeforeLayout target and we have no control over it. We need to define the following AfterLayout target:

123
Name="AfterLayout" Condition="'$(Configuration)'=='Release'">
Files="@(ExcludedFiles)" ManifestPath="$(ManifestPath)" />
view rawafter-layout.xml hosted with ❤ by GitHub
ExcludeFromManifest task reads manifest.xml file from pkg folder and removes ExcludedFiles items from it:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
TaskName="ExcludeFromManifest" TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
ParameterType="System.String" Required="true" />
ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
Include="System" />
Include="System.Core" />
Include="System.Xml" />
Include="System.Xml.Linq" />
Include="Microsoft.CSharp" />
Namespace="System" />
Namespace="System.Reflection" />
Namespace="System.IO" />
Namespace="System.Linq" />
Namespace="System.Xml.Linq" />
Namespace="System.Net" />
Namespace="Microsoft.Build.Framework" />
Namespace="Microsoft.Build.Utilities" />
 
Type="Fragment" Language="cs">
<![CDATA[
try
{
var excludedFiles = Files.Select(item => item.ItemSpec).ToArray();
 
var manifestPath = ManifestPath.TrimEnd(new[] { '\\' }) + @"\manifest.xml";
var manifestXml = File.ReadAllText(manifestPath);
var manifest = XElement.Parse(manifestXml);
var excludedElements =
manifest.Descendants().Where(element => element.Name.LocalName == "TemplateFile")
.Where(element => excludedFiles.Contains((string) element.Attribute("Location")));
 
excludedElements.Remove();
manifest.Save(manifestPath);
return true;
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
return false;
}
]]>
Please note that I added Condition="'$(Configuration)'=='Release'" condition to both targets, so only Release version of WSP will not contain excluded files, Debug version should stay untouched for development mode of course.

Here is the link to the whole build file:

https://gist.github.com/vadimi/5572446

You can just include it in your project, import it through   and modify ScriptsDir, ManifestPath, ExcludedFiles variables according to your project needs.

No comments: