Thursday, May 31, 2018

May Cycling

Just under 240 miles done this month (~770 YTD) -- no more precision due to odo battery exhaustion, so the summer bike will be starting from zero once again next time.

The month finally turned the corner from cool and wet with fortnightly bursts of winter, encouraging me out on a long ride to the south on the first Bank Holiday Monday, finally pushing the limits again after a couple of years. It reminded me why I don't go out in that direction much, since the journey gets forced through Saffron Walden, which is generally horrid in terms of long grinds on narrow busy roads.

The weather turning also meant that I spent much of the month catching up on the garden, rather than being out on two wheels; so only the one long ride so far this year.

Monday, May 14, 2018

`dotnet build`ing Eto.Forms projects

A couple of months back, Scott Hanselman blogged about the Eto.Forms cross-platform UI toolkit for .net. I filed that away for reference, and this weekend got around to giving it a whirl.

So I took the default new F# app and made a personalized little skeleton project (standard boiler-plate for the "About..." dialog, that sort of thing), and as part of making it re-usable as such, added a trivial FAKE build script. Eto.Forms generates new-style projects, so of course that would be using the and DotNet.publish tasks...

Not so fast.

Do that and it barfs with

error : MSB4801: The task factory "CodeTaskFactory" is not supported on the .NET Core version of MSBuild.
error MSB4175: The task factory "CodeTaskFactory" could not be loaded from the assembly "C:\Program Files\dotnet\sdk\2.1.200\Microsoft.Build.Tasks.Core.dll". The task factory must return a value for the "TaskType" property.

which turns out to be a known issue (or two). Rather than sit tight and wait, I did a bit of looking around. Turns out there's a similar, closed, issue against msbuild itself, to which the resolution is to use the RoslynCodeTaskFactory as a replacement.

So, after some experimentation, here's a work-around while a fix works its way through the system; it functions by overriding the _BuildAppBundle target, so can be fitted into existing build processes without hacking any of your downloaded packages.

  1. Add the RoslynCodeTaskFactory NuGet package to each affected project (probably just the .Desktop one
  2. Get the override target file MacTemplateOverride.targets from this gist
  3. Adjust the path to your eto.platform.mac64 version (assumed 2.4.1 in default location) as needed

  4. Add <Import Project="[path to]\MacTemplateOverride.targets" /> at the end of each affected project to load the file from where you've saved it in your solution

  5. dotnet build

Wednesday, May 09, 2018

Assembly versioning is hard -- internals just keep on leaking

I've hit a couple of instances of this in recent weeks.

Assembly versioning is meant to be a strong contract, that given two distinct instances with the same version, one can be used as a drop-in replacement for the other, e.g. as a a bug-fixing patch, in all circumstances. A sufficiently strong contract, indeed, that in my experience in building commercial product with .net, our process erred on the side of caution, and bumped the build number facet of the version every commit, even if all that had changed were dependencies.

However, it doesn't always work that way.

The first instance I hit was when Mono.Cecil finally hit 0.10 final after having been in beta for years. That had indeed preserved its public contract -- but somewhere between beta-7 and final, one of the internal APIs consumed by the symbol-reading helper assemblies via InternalsVisibleTo was expanded. Consequently, if a beta version was loaded (e.g. by a tool -- in particular the NUnit3 test adapter for .net core) ahead of the final, the result was a MissingMethodException when indirectly invoking that internal path. I specify .net core here, because the lack of AppDomain isolation is what puts the tool's use of beta-6 into the pot ahead of the system under test's final.

The most recent has been with Visual Studio 2017, which also bundles an update to the F# core assembly version, and the FSharpLint MSBuild task. Here, the F# compiler's choice of generating synthetic names for compiler generated classes by tagging them with @lineNumber comes into its own -- the new file version "2018.04.25.1" includes an internal synthetic type related to event handling called Microsoft.FSharp.Control.CommonExtensions+SubscribeToObservable@1693; in the earlier "2018.01.25.1" build, the corresponding type is Microsoft.FSharp.Control.CommonExtensions+SubscribeToObservable@1741 and asking for the former when the latter is the one on offer gets you a TypeLoadException. Here, it looks like AppDomain isolation is working against us in the other direction, with different and thus incompatible builds of FSharp.Code.dll being loaded in two separate AppDomains.