Damian Hickey

Mostly software and .NET related. Mostly.

Script to strip strong names from a project's references.

In my (never-ending) quest for true continous integrations nirvana, I find that version numbers in the a projects reference...

<Reference Include="System.Reactive, Version=1.0.2787.104, Culture=neutral, etc">
	<HintPath>..\..\libs\RxFramework\System.Reactive.dll</HintPath>
</Reference>

... sometimes obstruct me. Especially when dropping in a new version and attempting to build from a script.

This script below will recursively go through all the .csproj files from a base directory and strip out the strong name above, leaving:

<Reference Include="System.Reactive">
	<HintPath>..\..\libs\RxFramework\System.Reactive.dll</HintPath>
</Reference>

Le script:

param(
	[string]$path = $(throw "path required.")
)

[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
$csprojs = Get-ChildItem . -Recurse | Where-Object {$_.Extension -match "csproj"} | Select-Object FullName
$ns = "http://schemas.microsoft.com/developer/msbuild/2003"
foreach($csproj in $csprojs){
	$projectModified = $false
	$path = $csproj.FullName
	$csprojXmlDoc = [System.Xml.Linq.XDocument]::Load($path)
	$csprojXmlDoc.Descendants("{$ns}Reference") | 
		Where-Object {$_.Attribute("Include").Value.Contains(",") } | 
		ForEach { 
			$index = $_.Attribute("Include").Value.IndexOf(',')
			$newAttributeValue =  $_.Attribute("Include").Value.SubString(0, $index)
			Write-Host ($path + ": " + $_.Attribute("Include").Value + " -> $newAttributeValue")
			$_.Attribute("Include").Value = $newAttributeValue
			$projectModified = $true
			}
	if($projectModified -eq $true){
		$csprojXmlDoc.Save($path)
	}
}

Inconsistent Code Style annoys me

Honestly, I don't care if you are a spaces kinda person, or a tabs kinda person, or your class members start with 'm_' / '_' / nothing at all. What ever you choose, just be consistant throughout your code base. It makes your code readable and approachable, especially for someone new to the project. And if you have multiple people commiting use StyleCop to enforce a consistent style. Consistency trumps style preference each and every time. It really helps stave off code rot too.

This test method is from the NuGet source:

The NuGet project is well layed out and very approachable. When contributing to OSS project, you will have a better chance of getting your pull request accepted if it matches the maintainers style. Here we have different casing style for locally declared variables and an unclear rule as to when one should use var or not. All in same method so was probably written by same person. This add a barrier, albeit a small one, to contributing.

Shipping Code Contracts Reference Assemblies

Problem

So say I have SolutionA that has ProjectA that outputs:

    bin\Release\ProjectA.dll
    bin\Release\CodeContracts\ProjectA.Contracts.dll

A post build step on our CI server copies this build output to a specific LocationA ( a build artifact folder to be specific)

Then we have SolutionB, that has ProjectB, that has a reference to ProjectA in LocationA. The static checker picks ProjectA.Contracts.dll just fine. All good.

The build output from SolutionB is:

    bin\Release\ProjectA.dll
    bin\Release\ProjectB.dll
    bin\Release\CodeContracts\ProjectB.Contracts.dll

Note, ProjectA.Contracts.dll is not part of the build output. Post build step copies this output to LocationB.

Now, we have a third solution, SolutionC, that has ProjectC, that has explicit dependency on ProjectB and implicit dependency on ProjectA. Both of which are in LocationB.

The problem here is that static checker, when analyzing ProjectC, has no access to ProjectA.Contracts.dll and so has warnings.

What I think I would like is to have ProjectB's output look like:

    bin\Release\ProjectA.dll
    bin\Release\ProjectB.dll
    bin\Release\CodeContracts\ProjectA.Contracts.dll <- include this in output
    bin\Release\CodeContracts\ProjectB.Contracts.dll

And correspondingly, I'd like ProjectC's output to look like:

    bin\Release\ProjectA.dll
    bin\Release\ProjectB.dll
    bin\Release\ProjectC.dll
    bin\Release\CodeContracts\ProjectA.Contracts.dll
    bin\Release\CodeContracts\ProjectB.Contracts.dll
    bin\Release\CodeContracts\ProjectC.Contracts.dll

etc.

Solution

Add an AfterTarget the Microsoft.CodeContracts.targets file (%ProgramFiles(x86)%\Microsoft\Contracts\MsBuild\v4.0  and \3.5) that will copy it's code contracts reference assemblies, if they exist, to the output directory of the project being compiled:

<!--=====================================================================
   Copy reference's Contract Reference Assembles that are marked as "CopyLocal"
  ======================================================================-->
 <Target
   Name="CopyReferenceCodeContractReferenceAssemblies"
	 Condition="'@(ReferenceCopyLocalPaths)' != ''"
	 AfterTargets="CodeContractReferenceAssembly">
  <ItemGroup>
    <CodeContractsReferenceAssembliesFiles
	   Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)CodeContracts\%(Filename).Contracts%(Extension)')"
	   />
	  <CodeContractsReferenceAssembliesCopiedFiles/>
  </ItemGroup>
  <Copy
    SourceFiles="@(CodeContractsReferenceAssembliesFiles)"
    DestinationFolder="$(OutDir)CodeContracts"
    SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
    Retries="$(CopyRetryCount)"
    RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
    Condition="'$(UseCommonOutputDirectory)' != 'true' And Exists('%(RootDir)%(Directory)%(Filename)%(Extension)')"
    >
	  <Output TaskParameter="CopiedFiles" ItemName="CodeContractsReferenceAssembliesCopiedFiles"/>
	</Copy>
	<ItemGroup>
    <FileWrites Include="@(CodeContractsReferenceAssembliesCopiedFiles)"/>
	</ItemGroup>
 </Target>

This will need to be done on your build server too.

We have been using this at my company for several months now without issues.

Original discussion. Thx to Mike Barnett for the suggestion.

A NuGet (for tools) / OpenWrap (for libs) hybrid Visual Studio solution template

I've created a solution template as a that may form the basis of reference solution to be used in my company. It's available to view here

Requirements & Design decisions:

  1. To be used in a corp environment.
  2. No packages / wraps / dependencies / tools are committed to version control, except the bare minimum to bootstrap the build and/or restore dependencies.
  3. Packages / wraps / dependencies / tools may come from a variety of sources - nuget galley, local nuget repo, local openwrap repo.
  4. Anyone should be able to download the source and single click build the solution with no additional effort, friction or setup. (i.e. just run Build.ps1 )
  5. Easily managed via CI without addition setup and minimal coupling (i.e. .\Build.ps1 -buildNumber 2 )
  6. Sticking with released version of tools.
  7. I am using NuGet as my 'tools' manager. OpenWrap does not appear to be suitable for this purpose.
  8. OpenWrap seems to want to be responsible for compiling, running tests and creating the package. I already have psake for that. In my mind, creating a packages is a post-build step. Anyway, build: none to the rescue. 
  9. A psake build task sets the version number (using a build number if supplied) and keeps SharedAssemblyInfo.cs and the version file in sync.
  10. We use code contracts and the contract assemblies must be shipped in the wrap.

Some of the things I don't like:

  1. There is a bit too much going on in the root of the project, but at least Build.ps1 is at the top.
  2. Would rather not have to commit warps\openwrap-1.0.0.53270092.wrap to  support the bootstrap.
  3. ... and I'd like to add wraps/* to .hgignore.
  4. Can't isolate XUnit to only the DH.SolutionTemplate.Tests project (I know something is in the pipeline for this).
  5. Explicitly defining what goes into the resulting wrap may be a place where mistakes can easily happen. Not sure how to mitigate that.
  6. Having to use ;-shellInstall none; everywhere. I think o.exe default behaviour should be 'do nothing' and installation is an explicit step. I don't know enough of OpenWraps's architecture or intention to hold that belief firmly though :)
  7. In RestoreDependencies.ps1 I am deliberately adding the NuGet gallery remote source. This will add it permanently to the users remote list which I'd prefer not to affect. (I know there is something in OpenWrap's pipeline for this).

So, if there are any places this could be improved, let me know.

Using OpenWrap without having to install it

Well shortly after my last post on getting OpenWrap to work with TeamCity I was told that one can just include o.exe with your solution and use the switch

 

o -shellInstall none [rest of args]

 

instead.

This made me face-palm :)

In my defence, this switch is not shown via 'o', or 'o get-help'. Nor is it easily discoverable via the documentation / wiki when following links from openwrap.org. I also thought that because the default action of 'o' is to prompt you to install, I thought one had to.

And with that, I am of the opinion that 'o''s default action shouldn't prompt to install, and that installing should instead be an explicit switch ( '-install' ).

Getting TeamCity Build Agents and OpenWrap to work together

Just had some fun trying to get OpenWrap (1.0.0.53270093) working on TeamCity (6.0), and to be honest, I'm not entirely sure that I am doing it right. My build tool of choice is psake, and I am calling OpenWrap from within one of my build tasks.

Anyway, this is what I had to do and feedback is, of course, appreciated :)

1. Login to your build server with the same credentials your agents run under.

OpenWrap likes to install itself into the user specific application data folder, c:\Users\[User]\AppData\Local\openwrap, so you will need to login to your build server with the same credentials the TeamCity build agents run under. So they could access network resources, my agents run with domain credentials so this wasn't a problem.

2. Install OpenWrap with the 'i' option, "install the shell and make it available on the path?".

This will install OpenWrap in the folder mentioned above. The 'c' option, "use the current executable location and make it available on the path?" threw a DirectoryNotFoundException for me. Choosing 'n', "do nothing?" means that o.exe will continue to prompt you the installation questions.

This is where the fun begins. OpenWrap will add an entry to the user's (as opposed to the system's), PATH environment variable, creating the PATH variable if it didn't already exist. In my case, it didn't. Nothing wrong with this but there seems to a bug with TeamCity whereby, if the user's PATH environment variable is specified, the agent will use that and ignore the system one altogther. The expected behaviour would be to concatenate both.

So when restarting the agent, it's PATH enviroment variable went from this:

to this:

...and that broke a whole bunch of things!

3. Delete the User PATH environment variable.

Control Panel -> System -> Advanced Tab -> Environment Variables -> User variables for [username].

4. Modify buildAgent.properties

Open buildAgent.properties (typically located in C:\TeamCity\buildAgent\conf) in notepad and add the following line:

env.PATH=%env.PATH%;C\:\\Users\\[username]\\AppData\\Local\\openwrap

This is a workaround that adds the OpenWrap path to the system's PATH variable just for the use of the agent.

5. Restart Agent service

The agent's PATH variable should now be fixed:

And your build scripts should be able to call 'o' without problem.

 

Even though the bug is entirely lies with TeamCity, I do think OpenWrap could provide an better mechanism for such deployment scenarios. My system admin would prefer actually prefer MSI.. I don't know enough about OpenWrap's architecture perhaps an install for 'all users' would help?

If you spot anything wrong, or there is a better way, do let me know.

Patching OpenWrap descriptor with a branch name.

Firstly, why would would one want to do this? Well, it would depend on your team's setup. If you like to regularly create branches (for features, versions, spikes or otherwise), you may also like to have those branches built under CI. The problem is that it is not desirable to have the artifacts / packages of builds of these branches conflicting. Especially if you publish these artifacts to a shared location on a network drive.

Since two branches may have the same 'version', the best way (that I can come up with) to differentiate OpenWrap packages is based on the branch name, since that is usually guaranteed to be unique within a VCS.

At this point I am mostly experimenting, so I don't have this in active use, but this is how I am considering doing it. My build script tool of choice is PSake and the VCS is HG, but the concept should work for other setups.

Take an OpenWrap.desc file:

name: DH.Common
depends: Xunit
depends: ...

Add a field to the name and commit this:

name: DH.Common.%BranchName%
depends: Xunit
depends: ...

Add a build task to 'patch' the OpenWrap.desc file. This should be exected before the openwrap task:

task PatchOpenWrapDesc{
   $branchName = hg.exe branch
   Get-Content $rootDir\OpenWrap.desc | `
      Foreach-Object {$_ -replace "%BranchName%", "$branchName"} | `
      Set-Content $rootDir\OpenWrap.desc
}

This will result in a wrap called "DH.Common.branchName.wrap" and will be not clash with wraps created from other branches, by default.

If there is need to designate a branch as a 'release', removing the branch name patching is a dilberate step, which I prefer from an SOP point of view.

On ReSharper, object initializers, disposables and code analysis rule CA2000

Given a disposable class and a consumer:

public class DisposableClass : IDisposable
{
	public string Text { get; set; }

	public void Dispose()
	{}
}

public class Consumer
{
	public Consumer()
	{
		var disposableClass = new DisposableClass();
		disposableClass.Text = "S";
	}
}

ReSharper's quick fix will suggest to use object initializer:

Following this suggestion gives:

public class Consumer
{
	public Consumer()
	{
		var disposableClass = new DisposableClass { Text = "S" };
	}
}

But there is is a problem with this. If you run code analysis you will get the the following warning:

CA2000 : Microsoft.Reliability : In method 'Consumer.Consumer()', call System.IDisposable.Dispose on object '<>g__initLocal0' before all references to it are out of scope.

The problem is that under the hood, the compiler is creating a temporary local of DisposableClass, <>g_intLocal0, initializing it, then assigning it to variable disposableClass.

It's this local that it not getting disposed that causes the code analysis warning.

Perhaps the compiler could do a better job here but ReSharper could definitely do a better job:

  • Only suggest the object initializer quick fix for classes that are not disposable.
  • Include a quick fix to remove object initializers from disposable objects.

The issue has been reported, but infortunately JetBrains have marked it as "Won't Fix".

Edit 22/09/12: Looks like this will be fixed in Resharper 7.1.

Notify Property Weaver and Code Contracts

Notify Property Weaver is an AOP tool to convert automatic properties into properties that fire property changed events. I use code contracts, and since that also rewrites the outputted assemblies, I was wondering if there would be any conflict or issue.

Good news, my brief test (code below) tells me all is fine, though some more extensive usage would be required.

Couple of items I'd like to see in future versions of Notify Property Weaver:

  1. Dirty tracking. Which properties have changed. This would require the ability to 'load' a property with an initial value, without firing property changed event.
  2. Post-validation rules and IDataErrorInfo support.

Update: Simon Cropp, author of Notify Property Weaver, suggests a solution to validation and IDataErrorInfo using mixins.

namespace NotifyPropertyWeaverAndCodeContracts
{
	using System;
	using System.ComponentModel;
	using System.Diagnostics.Contracts;
	using NotifyPropertyWeaver;
	using Xunit;

	[ContractClass(typeof(FooContracts))]
	public interface IFoo
	{
		string Bar { get; set; }
	}

	[ContractClassFor(typeof(IFoo))]
	public class FooContracts : IFoo
	{
		public string Bar
		{
			get
			{
				return default(string);
			}
			set
			{
				Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(value));
			}
		}
	}

	public class Foo : IFoo, INotifyPropertyChanged
	{
		[NotifyProperty]
		public string Bar { get; set; }

		public event PropertyChangedEventHandler PropertyChanged;
	}

	public class FooTests
	{
		private readonly Foo _foo;

		public FooTests()
		{
			_foo = new Foo();
		}

		[Fact]
		public void When_set_Bar_to_null_Then_should_throw()
		{
			Assert.Throws<ArgumentException>(() => _foo.Bar = null);
		}

		[Fact]
		public void When_set_Bar_to_empty_Then_should_throw()
		{
			Assert.Throws<ArgumentException>(() => _foo.Bar = string.Empty);
		}

		[Fact]
		public void When_set_Bar_to_a_value_Then_should_raise_property_changed()
		{
			bool eventRaised = false;
			_foo.PropertyChanged += (sender, args) => eventRaised = args.PropertyName == "Bar";
			_foo.Bar = "a";
			Assert.True(eventRaised);
		}
	}
}

PowerShell script to restore a project's packages and tools

Requires nuget.exe to be in the solution root and commited to source control.

# Tools
.\NuGet.exe i DotCover -s \\myserver\Dev\NuGetPackages -o Tools
.\NuGet.exe i StyleCopCmd -s \\myserver\Dev\NuGetPackages -o Tools

# Dependencies
$packageConfigs = Get-ChildItem . -Recurse | where{$_.Name -eq "packages.config"}
foreach($packageConfig in $packageConfigs){
  Write-Host "Restoring" $packageConfig.FullName
  .\nuget.exe i $packageConfig.FullName -o Source\Packages
}