Wednesday 6 October 2010

Web Deployment Project - Post Build Events

In my current setup, I have a few web deployment projects which I use to easily compile my ascx user controls for shared use within other projects. With this setup I have cascading references to core web controls projects, to more specifically used web controls projects up the references chain.

With each project comes a standard assembly for the code behind, and an assembly which contains the compiled ascx information. Note: I am using the 'Merge all control outputs to a single assembly' option within the web deployment project property pages.

To save me having to include all of the projects within my main visual studio solution, I copy the compiled assemblies for each of these projects into a shared dependencies folder which can be referenced by many of my web applications. It was getting a bit of a pain to copy the files across to the dependencies folder each time I wanted to roll out a change, so I wanted to find a way to automate the process.

Deployment Projects Post Build Tasks
Within the web deployment projects, you can specify tasks to be run after build, these can include all of the macros from the standard post build tasks (see Web Application Project->Properties->Build Events->Edit Post Build->Macros >>) plus some addition tasks specific to the web deployment projects.

It is well documented that you can copy the compiled website output from the deployment project to another location, however I didn't want the whole website, I needed the merged assembly only.

Using naming conventions, I set the merged assembly to match the parent assembly name but with a '.compiled' addition e.g. 'myWebAssembly.dll' for the main project and 'myWebAssembly.Compiled.dll' for the web deployment project output.

With this in mind, I was able to put together a quick rule to identify the assemblies I wanted to copy using wilcards.

<Target Name="AfterBuild">
<ItemGroup>
<MySourceFiles Include="$(OutputPath)\**\*myWebAssembly*.dll"/>
</ItemGroup>

<Copy SourceFiles="@(MySourceFiles)" DestinationFiles="@(MySourceFiles->'..\Dependencies\%(Filename)%(Extension)')" />
</Target>

Minify JavaScript and CSS files
Within my compiled web controls projects, many of the script and CSS files used are added as embedded resources. One of the problems with this was that I wanted to minify the files when used as a dependency, but keep the originals, comments included, for the project itself. Luckily I found this great blog entry on automatically compressing embedded resources that goes through how to minify the embedded resource files and replace the originals within the assembly itself.

Note: To view the project file, unload your current web application project, then right click and select edit [myproject].csproj

<Import Project="$(MSBuildExtensionsPath)\Microsoft\MicrosoftAjax\ajaxmin.tasks" />
<PropertyGroup>
<ResGenDependsOn>
MinifyJavaScript;
$(ResGenDependsOn)
</ResGenDependsOn>
</PropertyGroup>
<Target Name="MinifyJavaScript" Condition=" '$(ConfigurationName)'=='Release' ">
<Copy SourceFiles="@(EmbeddedResource)" DestinationFolder="$(IntermediateOutputPath)" Condition="'%(Extension)'=='.js'">
<Output TaskParameter="DestinationFiles" ItemName="EmbeddedJavaScriptResource" />
</Copy>
<AjaxMin JSSourceFiles="@(EmbeddedJavaScriptResource)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".js" CssSourceFiles="@(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".css" JSLocalRenaming="CrunchAll" />
<ItemGroup>
<EmbeddedResource Remove="@(EmbeddedResource)" Condition="'%(Extension)'=='.js'" />
<EmbeddedResource Include="@(EmbeddedJavaScriptResource)" />
<FileWrites Include="@(EmbeddedJavaScriptResource)" />
</ItemGroup>
</Target>

Unfortunately this step seemed to be ignored when building through the web deployment project. I got around this by creating separate post build tasks, one for the web application project, and one for the web deployment project. I changed the web deployment project to only be responsible for the merged assembly using the naming convention above:

<Target Name="AfterBuild">
<ItemGroup>
<MySourceFiles Include="$(OutputPath)\**\*myWebAssembly.Compiled*.dll"/>
</ItemGroup>

<Copy SourceFiles="@(MySourceFiles)"
DestinationFiles="@(MySourceFiles->'..\Dependencies\%(Filename)%(Extension)')" />
</Target>

Post Build Tasks
Once I had updated the after build task above to only target the merged assembly, I then needed a way to copy the assembly from the main web project, including the minified embedded resources.

I added the following post build task to the main web application project through (Web Application Project->Properties->Build Events->Edit Post Build)

COPY $(TargetPath) ..\..\Dependencies\$(TargetFileName) /Y

This copies the assembly built for the current project into the same dependencies folder, and is fired when building the web deployment project (through the build dependencies).

Conclusion
In the end, my projects are building the assemblies, minifying the JavaScript and CSS, and copying to the dependencies folder ready to be referenced by my other projects. Just a few less things to worry about, which is always good in my book.

Further Reading
MS Build Task Reference
VS 2005 Web Deployment Projects
FAQ in ASP.NET Setup & Deployment
Web Deployment Projects: copy build output to a network share
Minify JavaScript and CSS files as Embedded Resources

No comments:

Post a Comment