.NET & Funky Fresh

Syndication

News

  • <script type="text/javascript" src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&amp;MarketPlace=US&amp;ID=V20070822/US/bluspiconinc-20/8001/8b68bf4b-6724-40e7-99a5-a6decf6d8648"> </script>
Building Caliburn.Micro’s NuGet Package

I’m pretty excited about having NuGet support for Caliburn.Micro with the upcoming RTW. The work was spearheaded by Ryan Cromwell and I’ve been testing it and extending it for about a week. I thought I would take this opportunity to share a few things that Ryan put together in order to make the package building process simple as well as show a few interesting aspects unique to the upcoming Caliburn.Micro package itself.

First, let’s look at a convenient PowerShell script Ryan wrote to make building the package easy:

function Get-ScriptDirectory {
  $Invoc = (Get-Variable MyInvocation -Scope 1).Value
  Split-Path $Invoc.MyCommand.Path 
}

# Hang on to current location
pushd . 

# Move to the Package working directory
$pkgroot = (Get-ScriptDirectory) + "\package"
cd $pkgroot

# Clear out the lib which are the references that will be added.
ls -Filter lib | del -recurse
mkdir lib

# Move the built binaries (release) into their respective convention folders
copy ..\..\bin\WPF\Release .\lib\Net40 -Recurse
copy ..\..\bin\Silverlight\Release .\lib\SL40 -Recurse
copy ..\..\bin\WP7\Release .\lib\SL40-wp -Recurse

..\nuget.exe pack caliburn.micro.nuspec

popd

Basically, all this script does is copy the release versions of the framework assemblies, along with their dependencies from a known location output by the build process to a location in our to-be-package folder structure. Then, it executes the nuget.exe tool in order to package those assemblies up based on the data found in our caliburn.micro.nuspec file. Here’s a look at that file:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>Caliburn.Micro</id>
    <version>1.0.0</version>
    <authors>Rob Eisenberg, Marco Amendola, Ryan Cromwell</authors>
    <description>A small, yet powerful framework designed for WPF, Silverlight and WP7, Caliburn.Micro implements a variety of UI patterns for solving real-world problems. Patterns that are highlighted include MVVM (Presentation Model), MVP and MVC.</description>
    <language>en-US</language>
    <licenseUrl>http://caliburnmicro.codeplex.com/license</licenseUrl>
    <projectUrl>http://caliburnmicro.codeplex.com</projectUrl>
    <iconUrl>http://www.caliburnproject.org/CaliburnIcon.png</iconUrl>
    <tags>MVVM Silverlight WPF WP7 SL4 SL MVC CoC Convention MVP PM Screen Coroutine Behavior Model-View-ViewModel Presentation UI ViewModel Phone Caliburn</tags>
    <frameworkAssemblies>
      <frameworkAssembly assemblyName="Microsoft.Phone.Controls" targetFramework="sl40-WP" />
      <frameworkAssembly assemblyName="System.ComponentModel.Composition" targetFramework="net40, sl40" />
      <frameworkAssembly assemblyName="System.ComponentModel.Composition.Initialization" targetFramework="sl40" />
    </frameworkAssemblies>
  </metadata>
</package>

This is all pretty standard stuff. You can read more about the specification for packages here. I want to point out something that is peculiar to our situation though. The frameworkAssemblies element was added as of NuGet 1.2. This allows you to easily specify which GAC’d assemblies should automatically be added to the project’s references at package install time. Notice how you can specify a targetFramework, thus allowing assemblies to be added conditionally by framework. Curiously, if I were to remove the first element indicating Microsoft.Phone.Controls, the package installation would fail for WP7 projects. The reason for this is that it picks up the System.ComponentModel.Composition element because it indicates sl40. Technically Windows Phone 7 is Silverlight, but that’s not what we want here…There appears to be a bug which shows itself when no WP7-specific assemblies are declared, but a SL4 assembly is. If that SL4 assembly doesn’t exist for WP7…boom! To solve the problem, all you need to do is add any assembly which would normally be part of the default WP7 project anyways.* That basically “tricks” NuGet into working right.

Because we are supporting multiple frameworks, we not only have custom framework assemblies and custom Caliburn.Micro assemblies, but our package installs custom content into your project. Unfortunately, NuGet 1.2 does not support that scenario out-of-the-box. Ryan came up with a pretty simple solution to the problem using the Tools folder. If you are not familiar, every NuGet package can have a custom Tools folder with various PowerShell scripts that will run during the installation or that can be made available to run at various times while you are working in Visual Studio. What Ryan did was create sub-folders under the Tools folder, one for each framework that we supported. Then, he wrote an install.ps1, which determines the framework, locates the framework specific content and adds it to your project. I then modified this to to query the project for it’s default namespace and do a little string replacement in those files before adding them to the project. Here’s the script:

param($rootPath, $toolsPath, $package, $project)

function get-content-path($contentRoot) {
    $moniker = $project.Properties.Item("TargetFrameworkMoniker").Value
    $frameworkname = new-object System.Runtime.Versioning.FrameworkName($moniker)

    $id = $frameworkname.Identifier

    if($id -eq ".NETFramework") { $relative = "NET40" }
    elseif($id -eq "Silverlight" -and $frameworkname.Profile -eq "WindowsPhone") { $relative = "SL40-WP" }
    elseif($id -eq "Silverlight" ) { $relative = "SL40" }
 
    [System.IO.Path]::Combine($contentRoot, $relative)
}
 
$contentSource = get-content-path($rootPath + "\tools")
$defaultNamespace = $project.Properties.Item("DefaultNamespace").Value

ls $contentSource | foreach-object { 
    $content = [System.IO.File]::ReadAllText($_.FullName)
    $content = $content.Replace('$safeprojectname$', $defaultNamespace)
    $content | out-file $_.FullName
    $project.ProjectItems.AddFromFileCopy($_.FullName)
}

$project.DTE.ItemOperations.Navigate('http://caliburnmicro.codeplex.com/wikipage?title=Nuget')

Since the Caliburn.Micro NuGet package still requires a little bit of configuration after installation, I created some custom documentation around that on codeplex and used the install.ps1 to open the VS browser to that page. That’s what the last line of the script does.

Working on this was an interesting learning experience for me and I’m looking forward to doing even more with Caliburn.Micro and NuGet in the future. If your open source projects need to target multiple framework or provide custom installation instructions, hopefully this will give you some ideas and save you some time.

 

*Microsoft.Phone.Controls is not part of the default WP7 project, but it turned out that I needed it there anyways, so I killed two birds with one stone.


Posted 04-08-2011 8:39 PM by Rob Eisenberg

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)