Hi Folks,
In this blog post we’ll cover how to set up automated (nightly?) builds on a Team Foundation 2010 Build Server for Windows Phone 7 projects.
The core challenge we’ll face is not having Visual Studio, Windows Phone 7 Tools or Windows Phone 7 SDK installed on the Build Server.
You can get a sample project with all the modifications discussed in this blog post at: http://JustinAngel.net/Storage/Justin.WindowsPhone.BuildFromLocalFiles.zip
Prerequisite Reading
There’s a blog post that I highly recommend you’ll read before embarking on this quest.
Building Silverlight 3 and Silverlight 4 applications on a .NET 3.5 build machine by Jeff Wilcox
In the linked article Jeff explains how to setup a TFS Build Server for Silverlight.
The blog post you are currently reading is heavily influenced by Mr. Wilcox approach to setting up a build server. Mainly because the writer of this blog post was schooled in the “Wilcox School of Application Packaging and offensive driving”.
If you’d like to setup a Continuous Integration in TFS for a Silverlight project, please read Mr. Wilcox’s article. This blog post only deals with WP7 projects.
The Problem – We don’t have WP7 Installed on a Build Server
Imagine this situation, you’re in a company that has multiple Windows Phone 7 projects on the same build server. Or maybe you’re hosting your TFS2010 Build server and multiple companies are using the same server.
Some project could be prototypes, and some projects could be completely different projects.
All of these projects are using different versions of the WP7 Tools.
If we were talking about Silverlight projects we would be talking about Silverlight 2, Silverlight 3 and Silverlight 4 projects.
In WP7’s case it would be different beta shipped at different months for the Windows Phone 7 developer tools.
However, since a server can only have 1 version installed at any given point in time – one project upgrading the server build version breaks all other projects.
For instance, in Vertigo we’ve got multiple Windows Phone 7 projects we’re working on for different customers.
We can’t guarantee that these multiple projects which are on different schedules with different deliverables will all upgrade together.
The Solution – Don’t install WP7 Tools at all, use local resources
Since the problem here is that our WP7 projects have a dependency on installed software, let’s make sure we sever all of these ties.
The ties between our projects and installed WP7 Tools are built into our MSBuild scripts. We’ll be focusing our efforts in this blog post on those MSBuild scripts.
1. Setup an “ExternalDependencies”/”ExternalTools” directory in source control.
As my own best practice, I establish a “Build” directory nested underneath that folder, and underneath it a “WP7_<Version>” folder. But these two last folders are just my own personal convention.
2. Drag & drop the contents of the WP7 MSBuild central directory into the “ExternalDependencies/Build/WP7_<Version>” folder.
On a x64 dev machine the folder is located on: C:\Program Files (x86)\MSBuild\Microsoft\Silverlight for Phone\v4.0
3. Create another nested folder named “Reference Assemblies” and copy the entire WP7 SDK into it.
On a x64 dev machine the WP7 SDK folder is located at: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone
4. In the folder holding the default *.targets files (“ExternalDependencies/Build/MSBuild_WP7_Apri10”), add a new <companyName.WP7.Version>.Targets file.
In this example the file is named “Vertigo.WP7.April10.targets”.
Paste in the following MSBuild script:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SilverlightBuildResources>$(CompileTimeSolutionDir)ExternalDependencies\Build\MSBuild_WP7_Apr10\</SilverlightBuildResources>
<TargetFrameworkDirectory>$(SilverlightBuildResources)Reference Assemblies\</TargetFrameworkDirectory>
<TargetFrameworkSDKDirectory>$(SilverlightBuildResources)Reference Assemblies\</TargetFrameworkSDKDirectory>
<_TargetFrameworkSDKDirectoryItem>$(SilverlightBuildResources)Reference Assemblies\</_TargetFrameworkSDKDirectoryItem>
<_FullFrameworkReferenceAssemblyPaths>$(SilverlightBuildResources)Reference Assemblies\;</_FullFrameworkReferenceAssemblyPaths>
<_TargetFrameworkDirectories>$(SilverlightBuildResources)Reference Assemblies\;</_TargetFrameworkDirectories>
<SilverlightRuntimeVersion>3.0.40624.0</SilverlightRuntimeVersion>
</PropertyGroup>
<Import Project="Microsoft.Silverlight.WindowsPhone.Overrides.targets" />
<Import Project="Microsoft.Silverlight.CSharp.targets" />
</Project>
5. Open up the “Microsoft.Silverlight.Common.targets” file (from “ExternalDependencies/Build/MSBuild_WP7_Apri10”) and comment out Lines 78-92.
The reason we have to do this edit, is because the default WP7 MSBuild script from Microsoft accidently uses registry settings instead of relaying on local values.
<!--<GetSilverlightFrameworkPath
RegistryBase="$(FrameworkRegistryBaseWithVersion)"
RuntimePathRegistryKey="$(RuntimePathRegistryKey)"
RuntimeVersionRegistryKey="$(RuntimeVersionRegistryKey)"
>
<Output TaskParameter="SilverlightPath" PropertyName="TargetFrameworkDirectory" Condition="'$(TargetFrameworkDirectory)' == ''"/>
<Output TaskParameter="SilverlightSDKPaths" ItemName="_TargetFrameworkSDKDirectoryItem" Condition="'$(TargetFrameworkSDKDirectory)' == ''"/>
<Output TaskParameter="SilverlightRuntimeVersion" PropertyName="SilverlightRuntimeVersion" Condition="'$(SilverlightRuntimeVersion)' == ''"/>
</GetSilverlightFrameworkPath>-->
<!-- Reset some items from the above task. Will remove when we merge with SL build system -->
<PropertyGroup>
<!--<_FullFrameworkReferenceAssemblyPaths>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToStandardLibraries($(TargetFrameworkIdentifier), $(TargetFrameworkVersion), ''))</_FullFrameworkReferenceAssemblyPaths>-->
<!--<TargetFrameworkDirectory>$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToStandardLibraries($(TargetFrameworkIdentifier), $(TargetFrameworkVersion), $(TargetFrameworkProfile)))</TargetFrameworkDirectory>-->
</PropertyGroup>
6. For every WP7 Project that needs to use local resources, we’ll have to edit the *.csproj file and replace the 2 default <Import> XML elements.
Right Click on your project and choose “Edit Project File”:
BTW, The “Edit Project file” context menu item comes from Visual Studio 2010 PowerCommands.
If you don’t have those installed, you’ll have to manually choose “Unload Project –> Edit File” on each project.
Thanks to Allen Newton who told me about PowerCommands!
In the *.csproj we’ll see the 2 default <Imports>:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.$(TargetFrameworkProfile).Overrides.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
We’ll comment out these <Import> elements and include our new <Import> with some flavouring to make Blend 4 RC to work with it well.
<!-- This MSBuild script compensates for Blend Design time having the wrong SolutionDir -->
<PropertyGroup>
<MissingExternalDependenciesBlendDesignTimehack>false</MissingExternalDependenciesBlendDesignTimehack>
<MissingExternalDependenciesBlendDesignTimehack Condition="!Exists('$(SolutionDir)ExternalDependencies')">true</MissingExternalDependenciesBlendDesignTimehack>
<CompileTimeSolutionDir Condition="'$(MissingExternalDependenciesBlendDesignTimehack)'!='true'">$(SolutionDir)</CompileTimeSolutionDir>
<CompileTimeSolutionDir Condition="'$(MissingExternalDependenciesBlendDesignTimehack)'=='true'">$(SolutionDir)..\</CompileTimeSolutionDir>
</PropertyGroup>
<Import Project="$(CompileTimeSolutionDir)ExternalDependencies\Build\MSBuild_WP7_Apr10\Vertigo.WP7.Apr10.targets" />
<!--<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.$(TargetFrameworkProfile).Overrides.targets" />-->
<!--<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />-->
7. We’re done.
Checkin to the server and as long as all files are in source control and all paths are correct the project will build on a server with no Windows Phone 7 Tools.
Setting up a new TFS Automated Build
In terms of setting up a new TFS2010 Build Definition for WP7, it’ll all plain old setup and pretty easy & straightforward.
There’s one major caveat we should call out: Make sure to set the build agent to X86.
If this step if not followed we’ll see a myriad of cryptic errors, like these ones:
If you’re interested in more information about this issue please read TFS2010 Build failed -- “Microsoft.Silverlight.Common.targets (104): The Silverlight 4 SDK is not installed” by Benjamin Day.
Summary
You can get a sample project with all the modifications discussed in this blog post @ http://JustinAngel.net/Storage/Justin.WindowsPhone.BuildFromLocalFiles.zip
Here’s a summary of the changes we needed to make in order for our project to run build successfully on a WP7-less TFS2010 Build server:
Fin
We’ve talked about why we it’s a smart move to avoid installing SDKs on our Build Server.
Additionally we’ve shown how to setup a WP7 project to run on a Build Server that doesn’t have WP7 Tools installed at all.
Hopefully this blog post will help you on your way to Continuous Integration for Windows Phone 7.
Leave a comment
What are your thoughts on this topic? Was this blog post useful?
Sincerely,
-- Justin Angel
Comments