Preparing Azure Cloud Service for running F# Compiler Service

28/09/2014

Over the last couple of weeks I have been spending my free time working on a small web application for running code challenges, that we are going to use when we visit universities to attract new candidates. I had three main objectives with the solution:

  1. The code challenges should be give the candidates an idea of the stuff we work with
  2. The code challenges should not be too hard to complete but also not trivial
  3. The code challenges should be fun to spent time on (although there is a price for the winner as well)

Last year we had a similar challenge, it was an offline challenge where you had to solve a problem in F#. Unfortunately either the language F# or the challenge itself was too intimidating so only one person solved it. In an attempt to avoid that this year, I wanted to make it so that the challenges can be coded at a computer and submitted to a website, for verification of whether or not it gives the correct result. To appeal to more people, there is a task to be solved in C# and one that is to be solved in F#, as we work with both technologies.

Building the web site framework for submitting code in C# and have the website compile it and execute it in a safe appdomain is fairly straight forward and has been done before, see e.g. Gobiner’s dotnetpad. Actually, I had built my own version using the Roslyn Compiler service before I found that solution, when I was searching for a good way to redirect console outputs.

At first my solution didn’t have support for F#, but one of my collegeauges suggested to add it, a challange I was up for. Little did I know that the compiler service for F# is currently undergoing a lot of rewriting, so it’s nowhere as easy to work with as the Roslyn compiler was for C#.

I used the hosted compiler sample as my starting point. I worked great, once I gave up on compiling to an dynamic assembly, and just compiled the source code to an assembly on disk, that I then read into memory before passing it to my Sandbox Appdomain runner. Also in order to run my compiled code I had to copy FSharp.Core.optdata and FSharp.Core.sigdata to the execution library together with the FSharp assemblies.
FsharpAssemblies.

With these few changes I was able to built a web application able to compile and run both C# and F# code challenges. The next problem occured when I wanted to put my solution into production. My plan was to publish it as a Azure Cloud Service, because of its ease and scalability. Unfortunately the Azure Cloud Service Instances are not prepared for running the F# Compiler Service (or the compiler service is not production ready, whatever the case). The problem that I experienced is described in this MSDN forum post. The error I got when trying to compile F# code was:

Could not resolve this reference. Could not locate the assembly "mscorlib.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245)
Could not resolve this reference. Could not locate the assembly "System.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Xml.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Runtime.Remoting.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Runtime.Serialization.Formatters.Soap.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Data.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Drawing.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Core.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Web.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Web.Services.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Windows.Forms.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Could not resolve this reference. Could not locate the assembly "System.Numerics.dll". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. (Code=MSB3245) Unable to find the file 'mscorlib.dll' in any of D:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0 d:\windows\system32\inetsrv

What this boils down to is that the F# compiler is unable to find a lot of framework assemblies, because it’s looking in the wrong places. Don Syme mentions in the forum post, that the fix is to install the Windows SDK when running on a machine without Visual Studio. So lets do that.

We could do that manually by accessing the server hosting the cloud service via remote desktop, but that wouldn’t be a scaleable solution, or a solution that would work when Microsoft decides to reprovision our service on a new host when doing maintenance.

In order to automatically install the SDK if it is missing, we need to include it (or a script that installs it) in our deployment package. Obviously opting for a script is the way to go in this situation.

The script, fsharp.cmd, looks like this:
Note if you copy paste this code, there has to be DOUBLE Space between 2010 and x64 and x86 for some reason that, is removed by my blog.

powershell -c "(new-object system.net.webclient).downloadfile("""http://download.microsoft.com/download/F/1/0/F10113F5-B750-4969-A255-274341AC6BCE/GRMSDKX_EN_DVD.iso""","""$pwd\GRMSDKX_EN_DVD.iso""")"

powershell -c "Mount-DiskImage -ImagePath $pwd\GRMSDKX_EN_DVD.iso"

wmic product where name="Microsoft Visual C++ 2010 x64 Redistributable - 10.0.40219" call uninstall /nointeractive
wmic product where name="Microsoft Visual C++ 2010 x86 Redistributable - 10.0.40219" call uninstall /nointeractive
start /wait f:\setup.exe -q -params:ADDLOCAL=ALL

The first line takes care of downloading the x64 Microsoft Windows SDK for Windows 7 and .NET Framework 4 (ISO). We need to download the entire ISO, as the alternative of using the web installer doesn’t support quite/”no user input” installation.
When the ISO is downloaded which do take a little while, we mount it.

Before we can install the SDK we need to remove some patches that will block the installation of the SDK. It is the Visual Studio C++ 2010 redistributables patches that must be uninstalled, we can uninstall them from the command line by calling wmic (to view all installed programs/updates type product get name).

After the patches have been removed and the ISO has been mounted we can run the setup.exe with the parameters for doing an unattended installation of everything in the SDK. When this finished the F# compiler service will be able to do its magic.

In order to run the script as part of the package installation add the following line to the ServiceDefinition.csdef
[xml]
<WebRole name="RoslynPlayground.Web" vmsize="Small">
<Startup>
<Task commandLine="fsharp.cmd" executionContext="elevated"></Task>
</Startup>

</WebRole>
[/xml]

You need to place the fsharp.cmd file in your web role project and be sure to remember to set “Copy to Output Directory” to “Copy Always”.

Now when you deploy your solution it will be F# compiler service ready.