«Older Posts - | rssFeed | My book on MSBuild and Team Build | Archives and Categories Tuesday, July 22, 2014

Stop checking-in binaries, instead create self-bootstrapping scripts

A few weeks ago Mads Kristensen and I created a few site extensions for Azure Web Sites which the Azure Image Optimizer and Azure Minifier. These extensions can be used to automatically optimize all images on a site, and minify all .js/.css files respectively. These are shipped as nuget packages in nuget.org as well as site extensions in siteextensions.net.

After creating those utilities we also update the image optimizer to support being called in on the command line via a .exe. We have not yet had a chance to update the minifier to be callable directly but we have an open issue on it. If you can help that would be great.

The exe for the image optimizer that can be used from the command line can be found in the nuget package as well. You can also download it from here, but to get the latest version you nuget.org is the way to go.

After releasing that exe I wanted an easy way to use it on a variety of machines, and to make it simple for others to try it out. What I ended up with is what I’m calling a “self-bootstrapping script” which you can find at optimize-images.ps1. Below you’ll see the entire contents of the script.

[cmdletbinding()]
param(
    $folderToOptimize = ($pwd),

    $toolsDir = ("$env:LOCALAPPDATA\LigerShark\tools\"),

    $nugetDownloadUrl = 'http://nuget.org/nuget.exe'
)

<#
.SYNOPSIS
    If nuget is in the tools
    folder then it will be downloaded there.
#>
function Get-Nuget(){
    [cmdletbinding()]
    param(
        $toolsDir = ("$env:LOCALAPPDATA\LigerShark\tools\"),

        $nugetDownloadUrl = 'http://nuget.org/nuget.exe'
    )
    process{
        $nugetDestPath = Join-Path -Path $toolsDir -ChildPath nuget.exe
        
        if(!(Test-Path $nugetDestPath)){
            'Downloading nuget.exe' | Write-Verbose
            (New-Object System.Net.WebClient).DownloadFile($nugetDownloadUrl, $nugetDestPath)

            # double check that is was written to disk
            if(!(Test-Path $nugetDestPath)){
                throw 'unable to download nuget'
            }
        }

        # return the path of the file
        $nugetDestPath
    }
}

<#
.SYNOPSIS
    If the image optimizer in the .ools
    folder then it will be downloaded there.
#>
function GetImageOptimizer(){
    [cmdletbinding()]
    param(
        $toolsDir = ("$env:LOCALAPPDATA\LigerShark\tools\"),
        $nugetDownloadUrl = 'http://nuget.org/nuget.exe'
    )
    process{
        
        if(!(Test-Path $toolsDir)){
            New-Item $toolsDir -ItemType Directory | Out-Null
        }

        $imgOptimizer = (Get-ChildItem -Path $toolsDir -Include 'ImageCompressor.Job.exe' -Recurse)

        if(!$imgOptimizer){
            'Downloading image optimizer to the .tools folder' | Write-Verbose
            # nuget install AzureImageOptimizer -Prerelease -OutputDirectory C:\temp\nuget\out\
            $cmdArgs = @('install','AzureImageOptimizer','-Prerelease','-OutputDirectory',(Resolve-Path $toolsDir).ToString())

            'Calling nuget to install image optimzer with the following args. [{0}]' -f ($cmdArgs -join ' ') | Write-Verbose
            &(Get-Nuget -toolsDir $toolsDir -nugetDownloadUrl $nugetDownloadUrl) $cmdArgs | Out-Null
        }

        $imgOptimizer = Get-ChildItem -Path $toolsDir -Include 'ImageCompressor.Job.exe' -Recurse | select -first 1
        if(!$imgOptimizer){ throw 'Image optimizer not found' }       

        $imgOptimizer
    }
}

function OptimizeImages(){
    [cmdletbinding()]
    param(
        [Parameter(Mandatory=$true,Position=0)]
        $folder,
        $toolsDir = ("$env:LOCALAPPDATA\LigerShark\tools\"),
        $nugetDownloadUrl = 'http://nuget.org/nuget.exe'
    )
    process{        
        [string]$imgOptExe = (GetImageOptimizer -toolsDir $toolsDir -nugetDownloadUrl $nugetDownloadUrl)

        [string]$folderToOptimize = (Resolve-path $folder)

        'Starting image optimizer on folder [{0}]' -f $folder | Write-Host
        # .\.tools\AzureImageOptimizer.0.0.10-beta\tools\ImageCompressor.Job.exe --folder M:\temp\images\opt\to-optimize
        $cmdArgs = @('--folder', $folderToOptimize)

        'Calling img optimizer with the following args [{0} {1}]' -f $imgOptExe, ($cmdArgs -join ' ') | Write-Host
        &$imgOptExe $cmdArgs

        'Images optimized' | Write-Host
    }
}

OptimizeImages -folder $folderToOptimize -toolsDir $toolsDir -nugetDownloadUrl $nugetDownloadUrl

The script is setup to where you call functions like Get-NuGet and GetImageOptimzer to get the path to the .exe to call. If the .exe is not in the expected location, under %localappdata% by default, it will be downloaded and then the path will be returned. In the case of this script I use nuget.org as my primary distribution mechanism for this so the script will first download nuget.exe and then use that to get the actual binaries. WIth this approach, you can avoid checking in binaries and have scripts which are still pretty concise.

After creating optimize-images.ps1 I thought it would be really useful to have a similar script to execute XDT transforms on xml files. So I created transform-xml.ps1. That script first downloads nuget.exe and then uses that to download the nuget packages which are required to invoke XDT transforms.

A self-bootstrapping script doesn’t need to be a PowerShell script, you can apply the same techniques to any scripting language. I’ve recently created an MSBuild script, inspired by Get-Nuget above, which can be used in a similar way. You can find that script in a gist here. It’s below as well.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="GetNuget">

  <!--
  This is an MSBuild snippet that can be used to download nuget to the path
  in the property NuGetExePath property.
  
  Usage:
   1. Import this file or copy and paste this into your build script
   2. Call the GetNuGet target before you use nuget.exe from $(NuGetExePath)
  -->
  
  <PropertyGroup>
    <NuGetExePath Condition=" '$(NuGetExePath)'=='' ">$(localappdata)\LigerShark\AzureJobs\tools\nuget.exe</NuGetExePath>
    <NuGetDownloadUrl Condition=" '$(NuGetDownloadUrl)'=='' ">http://nuget.org/nuget.exe</NuGetDownloadUrl>
  </PropertyGroup>
  
  <Target Name="GetNuget" Condition="!Exists('$(NuGetExePath)')">
    <Message Text="Downloading nuget from [$(NuGetDownloadUrl)] to [$(NuGetExePath)]" Importance="high"/>
    <ItemGroup>
      <_nugetexeitem Include="$(NuGetExePath)" />
    </ItemGroup>
    <MakeDir Directories="@(_nugetexeitem->'%(RootDir)%(Directory)')"/>
    <DownloadFile
        Address="$(NuGetDownloadUrl)"
        FileName="$(NuGetExePath)" />    
  </Target>

  <PropertyGroup Condition=" '$(ls-msbuildtasks-path)'=='' ">
    <ls-msbuildtasks-path>$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll</ls-msbuildtasks-path>
    <ls-msbuildtasks-path Condition=" !Exists('$(ls-msbuildtasks-path)')">$(MSBuildFrameworkToolsPath)\Microsoft.Build.Tasks.v4.0.dll</ls-msbuildtasks-path>
    <ls-msbuildtasks-path Condition=" !Exists('$(ls-msbuildtasks-path)')">$(windir)\Microsoft.NET\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll</ls-msbuildtasks-path>
  </PropertyGroup>
  
  <UsingTask TaskName="DownloadFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(ls-msbuildtasks-path)">
    <!-- http://stackoverflow.com/a/12739168/105999 -->
    <ParameterGroup>
      <Address ParameterType="System.String" Required="true"/>
      <FileName ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Reference Include="System" />
      <Code Type="Fragment" Language="cs">
        <![CDATA[
            new System.Net.WebClient().DownloadFile(Address, FileName);
        ]]>
      </Code>
    </Task>
  </UsingTask>
  
</Project>

This script has a single target, GetNuGet, which you can call to download nuget.exe to the expected location. After that you can use the path to nuget.exe from the NuGetExePath property. I’ve already removed nuget.ext from SideWaffle and AzureJobs repository using this technique. It’s a great way to avoid checking in nuget.exe.

 

Thanks,
Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

msbuild | powershell | scripting Tuesday, July 22, 2014 7:36:11 AM (GMT Daylight Time, UTC+01:00)  #     | 
Saturday, July 19, 2014

Introducing PSBuild – an improved interface for msbuild.exe in PowerShell

For the past few months I’ve been working on a project I’m calling PSBuild. It’s an open source project on GitHub which makes the experience of calling MSBuild from PowerShell better. Getting started with PSBuild is really easy. You can install it in one line.

(new-object Net.WebClient).DownloadString("https://raw.github.com/ligershark/psbuild/master/src/GetPSBuild.ps1") | iex

You can find this info on the project home page as well.

 

When you install PSBuild one of the functions that you get is Invoke-MSBuild. When you call Invoke-MSBuild it will end up calling msbuild.exe. Some advantages of using Invoke-MSBuild are.

Calling Invoke-MSBuild

A most basic usage of Invoke-MSBuild.

Invoke-MSBuild .\App.csproj

This will build the project using the default targets. The call to msbuild.exe on my computer is below.

C:\Program Files (x86)\MSBuild\12.0\bin\amd64\msbuild.exe 
    .\App.csproj 
    /m 
    /clp:v=m
    /flp1:v=d;logfile=C:\Users\Sayed\AppData\Local\PSBuild\logs\App.csproj-log\msbuild.detailed.log 
    /flp2:v=diag;logfile=C:\Users\Sayed\AppData\Local\PSBuild\logs\App.csproj-log\msbuild.diagnostic.log

From the call to msbuild.exe you can see that the /m is passed as well as a couple file loggers in %localappdata%. We will get to the logs later. More on Invoke-MSBuild,

To get a sense for how you can use Invoke-MSBuild take a look at the examples below.

# VisualStudioVersion and Configuration MSBuild properties have easy to use parameters
Invoke-MSBuild .\App.csproj -visualStudioVersion 12.0 -configuration Release

# How to pass properties
Invoke-MSBuild .\App.csproj -visualStudioVersion 12.0 -properties @{'DeployOnBuild'='true';'PublishProfile'='toazure';'Password'='mypwd-really'}

# How to build a single target
Invoke-MSBuild .\App.csproj -visualStudioVersion 12.0 -targets Clean

# How to bulid multiple targets
Invoke-MSBuild .\App.csproj -visualStudioVersion 12.0 -targets @('Clean','Build')

 

Getting log files

When you call Invoke-MSBuild on a project or solution the output will look something like the following.

image

Notice that line at the end. You can access your last log using the command.

Open-PSBuildLog

This will open the detailed log of the previous build in the program that’s associated with the .log extension. You can also use the Get-PSBuildLastLogs function to view the path for both log files written. If you want to view the folder where the log files are written to you can execute start (Get-PSBuildLogDirectory).

Helper functions

There are a couple of things that I’m constantly having to look up when I’m authoring MSBuild files; Reserved property names and escape characters. PSBuild has a helper function for each of these Get-MSBuildEscapeCharacters and Get-MSBuildReservedProperties. In the screenshot below you’ll see the result of executing each of these.

image

 

Default Properties

The Invoke-MSBuild cmdlet has a property –defaultProperties. You can pass in a hashtable just like the –properties parameter. These properties are applied as environment variables before the call to msbuild.exe and then reverted afterwards. The effect here is that you can have a property value which will be used if no other value for that property is specified in MSBuild.

 

There is so much more to PSBuild. This is just the tip of the iceberg. Keep an eye on the project page for more info. I’d love your help on this project. Please consider contributing to the project https://github.com/ligershark/psbuild.

 

Thanks,
Sayed Ibrahim Hashimi | http://msbuildbook.com/ | @SayedIHashimi

msbuild | psbuild Saturday, July 19, 2014 6:20:43 AM (GMT Daylight Time, UTC+01:00)  #     | 
Friday, May 09, 2014

How to download a site using msdeploy.exe

Recently I encountered a customer asking if it’s possible to download a site using msdeploy.exe. This is pretty easy using msdeploy.exe. I’ll demonstrate this with Microsoft Azure Web Sites but you can use this with any hosting provider that supports Web Deploy (aka MSDeploy).

To perform a sync with msdeploy.exe the structure of the command that we need to execute is as follows.

msdeploy.exe –verb:sync –source:<fill-in-details> –dest:<fill-in-details>

For the source property we will use the remote Azure Web Site, and for the dest property we will write to a folder on the local file system. You can get the Web Deploy publishing settings in the Azure Web Site by clicking on the Download the publish profile link in the Configure page.

image

This will download an XML file that has all the publish settings you’ll need. For example below is a publish settings file for my demo site.

<publishData>
  <publishProfile profileName="sayeddemo2 - Web Deploy" publishMethod="MSDeploy" 
                  publishUrl="waws-prod-bay-001.publish.azurewebsites.windows.net:443" 
                  msdeploySite="sayeddemo2" 
                  userName="$sayeddemo2" 
                  userPWD="*** removed ***" 
                  destinationAppUrl="http://sayeddemo2.azurewebsites.net" 
                  SQLServerDBConnectionString="" mySQLDBConnectionString="" hostingProviderForumLink="" 
                  controlPanelLink="http://windows.azure.com">
    <databases/>
  </publishProfile>
  <publishProfile profileName="sayeddemo2 - FTP" publishMethod="FTP" 
                  publishUrl="ftp://waws-prod-bay-001.ftp.azurewebsites.windows.net/site/wwwroot" ftpPassiveMode="True" 
                  userName="sayeddemo2\$sayeddemo2" userPWD="*** removed ***" 
                  destinationAppUrl="http://sayeddemo2.azurewebsites.net" SQLServerDBConnectionString="" 
                  mySQLDBConnectionString="" hostingProviderForumLink="" 
                  controlPanelLink="http://windows.azure.com">
    <databases/>
  </publishProfile>
</publishData>

The publish settings file provided by Azure Web Sites has two profiles; an MSDeploy profile and an FTP profile. We can ignore the FTP profile and just use the MSDeploy one. The relevant settings from the profile that we will use are the following values.

We will use the contentPath MSDeploy provider to download the files. On the source parameter we will need to include the relevant details of the remote machine. The full command to execute is below. I’ll break it down a bit after the snippet.

"C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe" 
    -verb:sync 
    -source:contentPath=sayeddemo2,
        ComputerName="https://waws-prod-bay-001.publish.azurewebsites.windows.net/msdeploy.axd?site=sayeddemo2",
        UserName=$sayeddemo2,Password=***removed***,AuthType='Basic'  
    -dest:contentPath=c:\temp\pubfromazure -disablerule:BackupRule

The important parts of the command above are how the remote settings are passed to the source provider. On the dest side I’ve provided the location where the files should be downloaded to.

 

Sayed Ibrahim Hashimi | http://msbuildbook.com/ | @SayedIHashimi

Friday, May 09, 2014 12:02:02 AM (GMT Daylight Time, UTC+01:00)  #     | 
Friday, October 11, 2013

SideWaffle: How to create your own VS template pack

If you haven’t heard I’m working on a project with Mads Kristensen called SideWaffle. SideWaffle is a Visual Studio Extension which contains many different Visual Studio Item and Project Templates. This is a community effort and all open source at https://github.com/ligershark/template-builder. You can create your own Item Templates and Project Templates and send a Pull Request for them to be included in the main repo. Check out the video below for more info on SideWaffle.

SideWaffle intro video

Item Templates are used by VS developers to create files using the Add New Item dialog. SideWaffle already contains a number of Item Templates such as; Angular Controller, robots.txt, SignalR Hub and Client, etc. For more info on how to create Item Templates with SideWaffle watch the 4 minute video below.

 

Project Templates are the items that show up in the Add New Project dialog. They provide a starting point for developers creating new projects. SideWaffle already has a few project templates as well, such as a Google Chrome Extension. You can learn more about how to create Project Templates in this video.

 

Now that we’ve gotten the intro out of the way, let’s explore how you can create your own SideWaffle.

How to create your own SideWaffle

The idea behind SideWaffle is that we will have a shared VS extension for popular VS Item and Project Templates. Instead of contributing to the main SideWaffle project you may be interested in creating your own distribution that does not have the standard templates. For example, I’ve heard from both the Orchard and Umbraco that they are interested in creating template packs for their communities. It wouldn’t make much sense to include those templates in the main SideWaffle project. Instead it would be best to create a separate distribution for each; OrchardWaffle and UmbracoWaffle.

So how can you do this? It’s pretty easy actually. SideWaffle is built on top of a NuGet package, TemplateBuilder, which is also open source at https://github.com/ligershark/template-builder. All the core functionality of SideWaffle is contained in that NuGet package. To create your own SideWaffle follow these steps:

After you add the TemplateBuilder NuGet package a few things happen:

  1. The build process of the project is modified to support building Item and Project templates
  2. Your .vsixmanifest file is updated with two new Asset tags
  3. An ItemTemplates folder is created with a sample Item Template

From here on you can build the project and after installing the generated .vsix you can have users easily create instances of your item or project templates.

You can add additional Item Templates, as well as create Project Templates in your project. That’s pretty much all there is to getting started with your own Waffle pack.

Let me know if you have any issues or comments.

Happy Waffleing!

 

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

extensibility | SideWaffle | Visual Studio | Visual Studio 2012 | VSIX Friday, October 11, 2013 5:11:26 PM (GMT Daylight Time, UTC+01:00)  #     | 
Saturday, September 21, 2013

How to extend the web publish process without modifying project contents

When automating web publishing for Visual Studio projects in many cases your first step will be to create a publish profile for the project in VS. There are many cases in which you cannot, or would not like to do this. In this post I’ll show you how you can take an existing project and use an MSBuild file to drive the publish process. In that I’ll also show how you can extend the publish process without modifying either the project or any of its contents.

Before we get too far into this, if you are not familiar with how to publish your VS web projects from the command line you can read our docs at ttp://www.asp.net/mvc/tutorials/deployment/visual-studio-web-deployment/command-line-deployment.

When you publish a project from the command line using a publish profile you typically use the syntax below.

msbuild .\MyProject.csproj /p:VisualStudioVersion=11.0 /p:DeployOnBuild=true /p:PublishProfile=<profile-name-or-path> 

In this snippet we are passing in a handful of properties. VisualStudioVersion dictates which version of MSBuild targets are used during the build. See http://sedodream.com/2012/08/19/VisualStudioProjectCompatabilityAndVisualStudioVersion.aspx for more details on that. DeployOnBuild=true injects the publish process at the end of build. PublishProfile can either be the name of a publish profile which the project contains or it can be the full path to a publish profile. We will use PublishProfile with that second option, the full path.

So we need to pass in the full path to a publish profile, which typically is a .pubxml file. A publish profile is just an MSBuild file. When you pass in PublishProfile and DeployOnBuild=true, then the publish profile is Imported into the build/publish process. It will supply the publish properties needed to perform the publish.

Let’s see how that works. I have a sample project, MySite, which does not have any publish profiles created for it. I have created a publish profile, ToFileSys.pubxml, in another folder that will be used though. The contents of that file are below.

ToFileSys.pubxml

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>FileSystem</WebPublishMethod>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <publishUrl>C:\temp\Publish\01</publishUrl>
    <DeleteExistingFiles>False</DeleteExistingFiles>
  </PropertyGroup>
</Project>

This publish profile will publish to a local folder. I just created this file in VS with a different project and then just copied it to the folder that I needed, and removed properties which are only used for the inside of VS experience. We can publish the MySite project using this profile with the command below.

msbuild MySite.csproj 
            /p:VisualStudioVersion=11.0 
            /p:DeployOnBuild=true 
            /p:PublishProfile=C:\data\my-code\publish-samples\publish-injection\build\ToFileSys.pubxml 

When you execute this the file specified in PublishProfile will be included into the build process.

Taking it up a level

steps

Now let’s see how we can take this to the next level by having a single script that will be used to publish more than one project using this technique.

In the sample files (which you can find links for at the end of the post). I have a solution with two web projects, MySite and MyOtherSite. Neither of these projects have any publish profiles created. I have created a script which will build/publish these projects which you can find at build\publish.proj in the samples. The contents of the file are shown below.

publish.proj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="BuildProjects">
  <!-- 
  This file is used in two ways.
    1. Drive the build and publish process
    2. It is used by the publish process during the build of MySite to configure/extend publish
        Note: 1. Is kicked off by the use on the cmd line/build server. 2. Is invoked by this script itself.
              This file is injected into the publish process via the PublishProfile property.
  -->  
  <PropertyGroup>
    <VisualStudioVersion Condition=" '$(VisualStudioVersion)'=='' ">11.0</VisualStudioVersion>
    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
    <!-- Location for build output of the project -->
    <OutputRoot Condition=" '$(OutputRoot)'=='' ">$(MSBuildThisFileDirectory)..\BuildOutput\</OutputRoot>
    <!-- Root for the publish output -->
    <PublishFolder Condition=" '$(PublishFolder)'==''">C:\temp\Publish\Output\</PublishFolder>    
  </PropertyGroup>
  
  <ItemGroup>
    <ProjectsToBuild Include="$(MSBuildThisFileDirectory)..\MySite\MySite.csproj">
      <AdditionalProperties>
        VisualStudioVersion=$(VisualStudioVersion);
        Configuration=$(Configuration);
        OutputPath=$(OutputRoot);
        WebPublishMethod=FileSystem;
        publishUrl=$(PublishFolder)MySite\;
        DeployOnBuild=true;
        DeployTarget=WebPublish;
        PublishProfile=$(MSBuildThisFileFullPath)
      </AdditionalProperties>
    </ProjectsToBuild>

    <ProjectsToBuild Include="$(MSBuildThisFileDirectory)..\MyOtherSite\MyOtherSite.csproj">
      <AdditionalProperties>
        VisualStudioVersion=$(VisualStudioVersion);
        Configuration=$(Configuration);
        OutputPath=$(OutputRoot);
        WebPublishMethod=FileSystem;
        publishUrl=$(PublishFolder)MyOtherSite\;
        DeployOnBuild=true;
        DeployTarget=WebPublish;
        PublishProfile=$(MSBuildThisFileFullPath)
      </AdditionalProperties>
    </ProjectsToBuild>
  </ItemGroup>
  
  <Target Name="BuildProjects">
    <MSBuild Projects="@(ProjectsToBuild)" />
  </Target>

  <!-- ***************************************************************************************
 The targets below will be called during the publish process.
 These targets are injected into the publish process for each web project.
 These targets will not have access to any new values for properties/items from
 the targets above this.
 *************************************************************************************** -->

  <Target Name="AfterWebPublish" AfterTargets="WebPublish">
    <Message Text="Inside AfterWebPublish" Importance="high"/>
  </Target>
  
</Project>

This file is pretty simple, it declares some properties which will be used for the build/publish process. Then it declares the projects to be built with an item list named ProjectsToBuild. When declaring ProjectsToBuild I use the AdditionalProperties metadata to specify MSBuild properties to be used during the build process for each project. Let’s take a closer look at those properties.

<AdditionalProperties>
  VisualStudioVersion=$(VisualStudioVersion);
  Configuration=$(Configuration);
  OutputPath=$(OutputRoot);
  WebPublishMethod=FileSystem;
  publishUrl=$(PublishFolder)MySite\;
  DeployOnBuild=true;
  DeployTarget=WebPublish;
  PublishProfile=$(MSBuildThisFileFullPath)
</AdditionalProperties>

I’ll explain all the properties now. VisualStudioVersion, Configuration and OutputPath are all used for the build process. The other properties are related to publishing. If you want to publish from the file system those properties (WebPublishMethod, publishUrl, DeployOnBuild, and DeployTarget) must be set. The most important property there is PublishProfile.

PublishProfile is set to $(MSBuildThisFileFullPath) which is the full path to publish.proj. This will instruct the build process of that project to import publish.proj when its build/publish process is started. It’s important to note that a “new instance” of the file will be imported. What that means is that the imported version of publish.proj won’t have access to any dynamic properties/items created in publish.proj.

The reason why PublishProfile is specified there is so that we can extend the publish process from within publish.proj itself. publish.proj has a target, AfterWebPublish, which will be executed after each project is executed. Let’s see how this works.

We can execute the publish process with the command below.

msbuild .\build\publish.proj /p:VisualStudioVersion=11.0

After executing this command the tail end of the result is shown in the image below.

image

In the image above you can see that the MyOtherSite project is being publish to the specified location in publish.proj and the AfterWebPublish target is executed as well.

 

In this post we’ve seen how we can use an MSBuild file as a publish profile, and how to extend the publish process using that same file.

You can download the samples at https://dl.dropboxusercontent.com/u/40134810/blog/publish-injection.zip. You can find the latest version in my publish-samples repository at publish-injection.

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

msbuild | Visual Studio | Visual Studio 2012 | web | Web Publishing Pipeline Saturday, September 21, 2013 7:57:03 PM (GMT Daylight Time, UTC+01:00)  #     | 
Saturday, June 08, 2013

Introducing VsixCompress–a NuGet package to compress your Visual Studio Package

A few weeks ago I blogged about a Visual Studio extension, Farticus, which I’m working on with Mads Kristensen. In that post I described how the default compression of a .vsix (the artifact that is created for a Visual Studio Package) is not as small as it could be. It’s better to get a fully compressed VSIX because when users install the component the download time can be significantly reduced. In that post I described how you could use the Zip task from the MSBuild Extension Pack to have a fully compressed .vsix file. I will now show you how I’ve simplified this.

Icon for package VsixCompressVsixCompress

Since my previous post I’ve created a NuGet package, VsixCompress which simplifies this greatly. If you have an existing Visual Studio package and want to have a fully compressed .vsix file then all you need to do is install the VsixCompress package.

image

 

After you install this package the following happens.

  1. NuGet package is downloaded and installed to the packages folder
  2. The project is edited to include an import for a new .targets file
  3. The build process is extended to compress the .vsix file automatically

After installing this package once you build the generated .vsix is much smaller than before. In the default case where you select to create a new C# VS Package the created .vsix is 17kb. After adding VsixCompress the resulting .vsix is only 9kb. That’s almost half the size. That’s all you need to know for local development. If you have a build server setup then there are a couple of additional steps. Let’s go over those now.

Build Server Support

I have blogged before about issues of shipping build updates in NuGet. To briefly summarize, when leveraging NuGet Package Restore you have to be careful if any of those NuGet packages have build updates. When using Package Restore the NuGet packages which contain the imported .targets file(s) are restored after the build starts. What this means is that the .targets files will never be imported (or an old copy is imported in the case the file exists from a previous build). The only way to work around this is to restore the packages before the .sln/.csproj file themselves are built. You can read the full details at http://sedodream.com/2012/12/24/SlowCheetahBuildServerSupportUpdated.aspx. I have a NuGet package, PackageRestore, which can help here. Take a look at my previous post How to simplify shipping build updates in a NuGet package. Now that we’ve discussed all the details that you need let’s discuss what my plans are going forward with this.

Plans going forward

I’m hoping to add the following features over the course of the next few weeks.

FYI VsixCompress is open source so you can take a look at the code, or better yet contribute at https://github.com/sayedihashimi/vsix-compress.

Please let me know what you think of this!

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

extensibility | Visual Studio | VSIX Saturday, June 08, 2013 1:42:57 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, June 06, 2013

How to simplify shipping build updates in a NuGet package

Have you ever wanted to ship build updates in a NuGet package? If so then you may have noticed that there are issues if you need to import a .targets file which is contained in a NuGet package. To be brief, the issue is that by the time the .targets file is being restored with NuGet it’s already too late because the build has already started. So we need an easy way to invoke NuGet package restore for a given project before the build for the solution/project starts. In this entry you will see how we can use a generated build script to help with this.

A few months back I discovered this issue and blogged about it at SlowCheetah build server support updated. The solution that I implemented was to create a new build script, packageRestore.proj, which is placed in the root of the project when the SlowCheetah NuGet package is installed. If you want to invoke the package restore from a build server then you can just add packageRestore.proj to the list of items to build before the .sln/.csproj file which you are intending to build. I’ve now refactored this into its own NuGet package, PackageRestore.

 

Here is how you can try it out.

  1. Create a new project
  2. Add a few NuGet packages to the project
  3. Enable NuGet package restore
  4. Add the PackageRestore package (the command you can use is Install-Package PackageRestore –pre)
  5. Close solution
  6. Delete packages folder
  7. Execute the command msbuild.exe packageRestore.proj

After executing the command you will see that the packages folder along with all its content will be restored. For CI servers when you define your build script all you do is build packageRestore.proj before you build the target .sln/.csproj file.

If you are shipping build updates through NuGet and want to be able to enable package restore in a similar way a SlowCheetah then when you create your NuGet package add PackageRestore as a dependency.

 

This is an open source project so you can view the source, and help contribute, at https://github.com/sayedihashimi/package-restore.

 

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

msbuild | nuget | PackageRestore Thursday, June 06, 2013 6:57:37 AM (GMT Daylight Time, UTC+01:00)  #     | 
Wednesday, June 05, 2013

How to publish a VS web project with a .publishSettings file

The easiest way to publish a Visual Studio web project from the command line is to follow the steps below.

  1. Open VS
  2. Right click on the Web project and select Publish
  3. Import your .publishSettings file (or manually configure the settings)
  4. Save the profile (i.e. .pubxml file)
  5. From the command line publish with the command line passing in PublishProfile

For more details on this you can see ASP.NET Command Line Deployment. This is pretty simple and very easy, but it does require that you manually create the .pubxml file. In some cases you’d just like to download the .publishsettings file from your hosting provider and use that from the command line. This post will show you how to do this.

In order to achieve the goal we will need to extend the build/publish process. There are two simple ways to do this; 1. Place a .wpp.targets file in the same directory as the web project or 2. Pass an additional property indicating the location of the .wpp.targets file. I’ll first go over the technique where you place the file directly inside of the directory where the project is. After that I’ll show you how to use this file from a well known location.

One way to do this is to create a .wpp.targets file. This .wpp.targets file will be imported into the build/publish process automatically. This .targets file will enable us to pass in PublishSettingsFile as an MSBuild property. It will then read the .publishsettings file and output the properties needed to execute the publish process.

.wpp.targets in the project directory

Let’s take a look at the .targets file and then we will discuss it’s contents. Below you will find the contents of the full file.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
  
  <!--
  When using this file you must supply /p:PublishSettingsFile as a parameter and /p:DeployOnBuild=true
  -->  
  <PropertyGroup Condition=" Exists('$(PublishSettingsFile)')">
    <!-- These must be declared outside of a Target because they impact the Import Project flow -->
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <DeployTarget>WebPublish</DeployTarget>
  
    <PipelineDependsOn>
      GetPublishPropertiesFromPublishSettings;
      $(PipelineDependsOn);
    </PipelineDependsOn>
  </PropertyGroup>

  <Target Name="GetPublishPropertiesFromPublishSettings" BeforeTargets="Build" Condition=" Exists('$(PublishSettingsFile)')">
    <PropertyGroup>
      <_BaseQuery>/publishData/publishProfile[@publishMethod='MSDeploy'][1]/</_BaseQuery>
      <!-- This value is not in the .publishSettings file and needs to be specified, it can be overridden with a cmd line parameter -->
      <!-- If you are using the Remote Agent then specify this as RemoteAgent -->
      <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    </PropertyGroup>

    <ItemGroup>
      <_MSDeployXPath Include="WebPublishMethod">
        <Query>$(_BaseQuery)@publishMethod</Query>
      </_MSDeployXPath>

      <_MSDeployXPath Include="MSDeployServiceURL">
        <Query>$(_BaseQuery)@publishUrl</Query>
      </_MSDeployXPath>

      <_MSDeployXPath Include="SiteUrlToLaunchAfterPublish">
        <Query>$(_BaseQuery)@destinationAppUrl</Query>
      </_MSDeployXPath>

      <_MSDeployXPath Include="DeployIisAppPath">
        <Query>$(_BaseQuery)@msdeploySite</Query>
      </_MSDeployXPath>

      <_MSDeployXPath Include="UserName">
        <Query>$(_BaseQuery)@userName</Query>
      </_MSDeployXPath>

      <_MSDeployXPath Include="Password">
        <Query>$(_BaseQuery)@userPWD</Query>
      </_MSDeployXPath>
    </ItemGroup>

    <XmlPeek XmlInputPath="$(PublishSettingsFile)"
             Query="%(_MSDeployXPath.Query)"
             Condition=" Exists('$(PublishSettingsFile)')">
      <Output TaskParameter="Result" PropertyName="%(_MSDeployXPath.Identity)"/>
    </XmlPeek>
  </Target>
</Project>

You can place this file in the root of your project (next to the .csproj/.vbproj file) with the name {ProjectName}.wpp.targets.

This .targets file is pretty simple. It defines a couple properties and a single target, GetPublishPropertiesFromPublishSettings. In order to publish your project from the command line you would execute the command below.

msbuild.exe MyProject /p:VisualStudioVersion=11.0 /p:DeployOnBuild=true /p:PublishSettingsFile=<path-to-.publishsettings>

Here is some info on the properties that are being passed in.

The VisualStudioVersion property indicates that we are using the VS 2012 targets. More info on this at http://sedodream.com/2012/08/19/VisualStudioProjectCompatabilityAndVisualStudioVersion.aspx.

DeployOnBuild, when true it indicates that we want to publish the project. This is the same property that you would normally pass in.

PublishSettingsFile, this is a new property which the .targets file recognizes. It should be set to the path of the .publishSettings file.

 

The properties at the top of the .targets file (WebPublishMethod and DeployTarget) indicate what type of publish operation is happening. The default values for those are MSDeploy and WebPublish respectively. You shouldn’t need to change those, but if you do you can pass them in on the command line.

The GetPublishPropertiesFromPublishSettings target uses the XmlPeek task to read the .publishsettings file. It then emits the properties required for publishing.

OK this is great an all, but it still requires that an additional file (the .wpp.targets file) be placed in the directory of project. It is possible to avoid this as well. Let’s move to that

.wpp.targets from a well known location

If you’d like to avoid having to place the .wpp.targets file in the directory of the project you can easily do this. Place the file in a well known location and then execute the same msbuild.exe call adding an additional property. See the full command below.

msbuild.exe MyProject /p:VisualStudioVersion=11.0 /p:DeployOnBuild=true /p:PublishSettingsFile=<path-to-.publishsettings> /p:WebPublishPipelineCustomizeTargetFile=<path-to.targets-file>

Once you do this you no longer need to create the Publish Profile in VS if you want to publish from the command line with a .publishsettings file.

 

FYI you can find the complete sample at https://github.com/sayedihashimi/publish-samples/tree/master/PubWithPublishSettings.

 

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

msbuild | web | Web Publishing Pipeline Wednesday, June 05, 2013 6:01:34 PM (GMT Daylight Time, UTC+01:00)  #     | 
Saturday, June 01, 2013

Hijacking the Visual Studio Build Process

Have you ever wanted to use Visual Studio to manage project artifacts but wanted to have a fully custom build process? The recommend way to do this is to build a Custom Visual Studio Project System, but there is a much easier way for lightweight needs. In this post I’ll show you how to take an existing project and “replace” the build process used? For example, wouldn’t it be cool if you could develop a Chrome Extension with VS? When you do a build it would be great to generate the .zip file to for the Chrome Gallery in the output folder. Doing this is way easier than you might think.

Before I go over the steps to do this let me explain the “contract” between Visual Studio and MSBuild. The primary interactions around build in Visual Studio include the following actions in VS. Including what VS does when the action is invoked.

For more details you can read Visual Studio Integration (MSBuild). The easiest way to completely customize the VS build process is to do the following:

  1. Create the correct project based on the artifacts you plan on using (i.e. if you’re going to be editing .js files then create a web project)
  2. Edit the project file to not import any of the .targets files
  3. Define the following targets; Build, Rebuild and Clean

After that when you invoke the actions inside of VS the correct action will be executed in your project.

Let’s look at a concrete example. I’ve been working on a Chrome extension with Mads Kristensen the past few days. The entire project is available on github at https://github.com/ligershark/BestPracticesChromeExtension. When it came time to try out the extension or to publish it to the Chrome store we’d have to manually create it, which was annoying. We were using a standard web project to begin with. Here is how we made the workflow simple. Edited the project file to disable the Import statements. See below.

<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" 
    Condition="false" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" 
    Condition="false and '$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" 
    Condition="false" />

For each of these targets I added/updated the condition to ensure that the .targets file would never be loaded. The reason why I did not simply remove these is because in some cases VS may get confused and try to “upgrade” the project and re-insert the Import statements.

After that I added the following statements to the .csproj file.

  <PropertyGroup>
    <LigerShareChromeTargetsPath>$(BuildFolder)\ligersharek.chrome.targets</LigerShareChromeTargetsPath>
  </PropertyGroup>
  <Import Project="$(LigerShareChromeTargetsPath)" Condition="Exists($(LigerShareChromeTargetsPath))" />

Here I defined a new property to point to a custom .targets file and a corresponding Import statement. Now let’s take a look at the ligershark.chrome.targets file in its entirety.

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <UseHostCompilerIfAvailable>false</UseHostCompilerIfAvailable>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <AppFileNameItem>
      <Visible>false</Visible>
    </AppFileNameItem>
    <AppFolderItem>
      <Visible>false</Visible>
    </AppFolderItem>
  </ItemDefinitionGroup>
  
  <UsingTask AssemblyFile="$(BuildLib)MSBuild.ExtensionPack.dll" TaskName="MSBuild.ExtensionPack.Compression.Zip" />
  
  <Target Name="Build">
    <MakeDir Directories="$(OutputPath)" />
    
    <ItemGroup>
      <_AppCandidateFilesToZip Remove="@(_AppCandidateFilesToZip)" />
      <_AppCandidateFilesToZip Include="@(Content);@(None)" />
    </ItemGroup>
    
    <FindUnderPath Path="$(AppFolder)" Files="@(_AppCandidateFilesToZip)">
      <Output TaskParameter="InPath" ItemName="_AppFilesToZip" />
    </FindUnderPath>
    <Message Text="App files to zip: @(_AppFilesToZip->'%(RelativeDir)%(Filename)%(Extension)')" />

    <PropertyGroup>
      <_AppFolderFullPath>%(AppFolderItem.FullPath)</_AppFolderFullPath>
    </PropertyGroup>

    <Message Text="Creating package .zip at [%(AppFileNameItem.FullPath)]" Importance="high" />
    <MSBuild.ExtensionPack.Compression.Zip 
      TaskAction="Create" 
      CompressFiles="@(_AppFilesToZip)" 
      ZipFileName="%(AppFileNameItem.FullPath)" 
      RemoveRoot="$(_AppFolderFullPath)" 
      CompressionLevel="BestCompression" />
  </Target>
  
  <PropertyGroup>
    <RebuildDependsOn>
      Clean;
      Build;
    </RebuildDependsOn>
  </PropertyGroup>
  <Target Name="Rebuild" DependsOnTargets="$(RebuildDependsOn)" />
  <Target Name="Clean">
    <!-- delete all the files in the output folder -->
    <ItemGroup>
      <_FilesToDelete Remove="@(_FilesToDelete)" />
      <_FilesToDelete Include="$(OutputPath)**\*" />
    </ItemGroup>
    <Message Text="Deleting files: @(_FilesToDelete)" />
    <Delete Files="@(_FilesToDelete)" />
  </Target>
</Project>

As you can see I defined the following targets; Build, Rebuild, and Clean. This is what we discussed earlier as the requirements. Now when I click the Build button in VS a .zip file containing the Chrome Extension is created in the bin\ folder. Additionally no other step (i.e. any typical build step) is performed here. The only thing happening is what is defined in the Build target here. Similarly when I Rebuild or Clean the Rebuild or Clean target is invoked respectively. This is  a pretty neat way to modify an existing project to completely replace the build process to suit your needs.

There are a few things in the .targets file that I’d like to point out.

UseHostCompilerIfAvailable

In the .targets file I have set the property declaration <UseHostCompilerIfAvailable>false</UseHostCompilerIfAvailable>. When you are working with a project in Visual Studio there is a compiler, the host compiler, that Visual Studio has which can be used. For performance reasons in most cases this compiler is used. In this case since we do not want any standard build actions to be executed this performance trick is undesired. You can easily disable this by setting this property to false.

After this MSBuild will always be invoked when performing any build related action.

Visible metadata for Item Lists

When using MSBuild you typically represent files as values within an item list. One issue that you may encounter when modifying a project which will be loaded in VS is that the files inside of those item lists will show up in Visual Studio. If you want to disable this for a particular value in an item list you can add the well known metadata <Visible>false</Visible>. For example you could have the following.

<ItemGroup>
    <IntermediateFile Include="cache.temp">
        <Visible>false</Visible>
    </IntermediateFile>
</ItemGroup>

If you don’t want to add this to every value in the item list then you can set the default value using an ItemDefinitionGroup.  For example take a look at the elements below.

<ItemDefinitionGroup>
  <AppFileNameItem>
    <Visible>false</Visible>
  </AppFileNameItem>
  <AppFolderItem>
    <Visible>false</Visible>
  </AppFolderItem>
</ItemDefinitionGroup>

After this declaration is processed if the Visible metadata is accessed for AppFileNameItem or AppFolderItem on a value which does not declare Visible, then false is returned.

 

The rest of the content of the .targets file is pretty straightforward. This post shows a basic example of how you can take an existing Visual Studio project and completely replace the build process.

 

This project is open source at https://github.com/ligershark/BestPracticesChromeExtension.

 

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

msbuild | MSBuild 4.0 | Visual Studio | Visual Studio 2012 Saturday, June 01, 2013 6:47:16 PM (GMT Daylight Time, UTC+01:00)  #     | 
Monday, May 13, 2013

How to compress a Visual Studio Extension (VSIX) during build

Lately I’ve been working with Mads Kristensen on a cool new Visual Studio Extension, Farticus, and wanted to share with you guys one of the things that I learned.

When you are developing Visual Studio extension on of the things you should keep in mind is the download size of the extension. This is the size of the .vsix file which you upload to the gallery. If the download size is too large typically people may get impatient and cancel the install. Because of this it’s a good idea to try and get your .vsix to the smallest size possible.

The good news here is that the .vsix file is already compressed. It’s actually a .zip file renamed to .vsix. If you want to see what’s in a .vsix just rename it to .zip and extract it out. The bad news is that the CreateZipPackage task used by the Microsoft.VsSdk.targets file does not perform the best compression. Fortunately there are tasks to create Zip files that we can use. I have chosen to use the Zip task from the MSBuild Extension Pack.

Before I go over all the details let’s take a look at what the final result will be after we compress with the MSBuild Extension Pack. See the table below for the comparison for two different projects.

Project

VSIX Size Before

VSIX Size After

Reduction

Farticus 442 kb 248 kb 43 %
VS Web Essentials 2102 kb 740 kb 65 %

From the table above we can see that we can gain a significant amount of additional compression by using the MSBuild extension pack.

Below is the content that I pated into the .csproj file. I’ll paste it in it’s entirety and then explain.

<PropertyGroup>
  <EnableCompressVsix Condition=" '$(EnableCompressVsix)'=='' ">true</EnableCompressVsix>
  <BuildLib Condition=" '$(BuildLib)'=='' ">$(MSBuildProjectDirectory)\..\Build\Lib\</BuildLib>

</PropertyGroup>
<UsingTask AssemblyFile="$(BuildLib)MSBuild.ExtensionPack.dll" TaskName="MSBuild.ExtensionPack.Compression.Zip"/>

<Target Name="CompressVsix" 
        AfterTargets="CreateVsixContainer" 
        DependsOnTargets="PrepareReplceVsixTemp" 
        Condition=" '$(EnableCompressVsix)'=='true' ">
    
  <!-- copy the file to the obj folder and then party on it -->
  <MakeDir Directories="$(_TmpVsixDir);$(_TmpVsixDir)\Extracted\"/>

  <Copy SourceFiles="$(TargetVsixContainer)"
        DestinationFolder="$(_TmpVsixDir)">
    <Output TaskParameter="CopiedFiles" ItemName="_TmpVsixCopy"/>
  </Copy>
    
  <!-- extract out the .zip file -->
  <MSBuild.ExtensionPack.Compression.Zip 
    TaskAction="Extract" 
    ExtractPath="$(_TmpVsixDir)Extracted\" 
    ZipFileName="@(_TmpVsixCopy->'%(FullPath)')"/>

  <ItemGroup>
    <_FilesToZip Remove="@(_FilesToZip)"/>
    <_FilesToZip Include="$(_TmpVsixDir)Extracted\**\*"/>
  </ItemGroup>

  <MSBuild.ExtensionPack.Compression.Zip
    TaskAction="Create"
    CompressFiles="@(_FilesToZip)"
    ZipFileName="%(_TmpVsixCopy.FullPath)"
    RemoveRoot="$(_TmpVsixDir)Extracted\"
    CompressionLevel="BestCompression" />

  <Delete Files ="$(TargetVsixContainer)"/>
  <Copy SourceFiles="%(_TmpVsixCopy.FullPath)" DestinationFiles="$(TargetVsixContainer)" />
</Target>

<Target Name="PrepareReplceVsixTemp" DependsOnTargets="CreateVsixContainer">
  <ItemGroup>
    <_VsixItem Remove="@(_VsixItem)"/>
    <_VsixItem Include="$(TargetVsixContainer)" />

    <_TmpVsixPathItem Include="$(IntermediateOutputPath)VsixTemp\%(_VsixItem.Filename)%(_VsixItem.Extension)"/>
  </ItemGroup>
    
  <PropertyGroup>
    <_TmpVsixDir>%(_TmpVsixPathItem.RootDir)%(_TmpVsixPathItem.Directory)</_TmpVsixDir>
  </PropertyGroup>

  <RemoveDir Directories="$(_TmpVsixDir)"/>  
</Target>  

 

The snippet above perform the following actions.

  1. Remove the old VsixTemp folder from any previous build if it exists
  2. Copy the source .vsix to the intermediate output path (i.e. obj\debug or obj\release)
  3. Extract out the contents to a folder
  4. Re-zip the file using MSBuild extension pack
  5. Replace the output .vsix with the compressed one

 

When building now the .vsix in the output folder should be smaller than it was before.

In my case I have copied the necessary assemblies from the MSBuild Extension pack and placed them in the projects repository. For the Zip task you’ll need the following files.

In my case I’ve placed them in a folder named Build\lib\.

You should be able to copy/paste what I have here into your VSIX projects. You’ll need to update the path to the MSBuild extension pack assemblies if you put them in a different location. Note: I’ve only tested this with Visual Studio 2012.

The MSBuild elements used here is pretty straight forward. If you have any questions on this let me know.

 

You can find the source for the Farticus project at https://github.com/ligershark/Farticus/. Please send us a Pull (my finger) Request.

 

Thanks,
Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

Visual Studio 11 | vs-extension | VSIX Monday, May 13, 2013 1:11:54 AM (GMT Daylight Time, UTC+01:00)  #     |