Sunday, December 6, 2009

F# on the Xbox 360

This article describes how to build an XNA Game Studio application using an F# library in such a way that it can be run on the Xbox 360. It applies to the current version of F#,

A method was first described in this blog by Jack Palevich. Most of his blog post still applies, except for step 2.

In order for an .net assembly to be usable on the Xbox 360, it must be linked against dlls for the Xbox 360, which can be found in <Program Files directory>\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360.

In particular, it must be linked against mscorlib.dll for the Xbox 360, which is the difficult part.

By default, when you create an F# library in Visual Studio, it is linked against FSharp.Core.dll, which references mscorlib for the PC.

In order to avoid conflicts between versions of mscorlib, one must build an F# library targeted at the Compact Framework 2.0, which is what the XNA .net framework is built upon.

The problem is that neither Visual Studio nor msbuild support this for F# libraries. The only solution is to compile the F# library using the F# compiler directly:

"C:\Program Files\FSharp-\bin\fsc.exe"
--standalone --noframework --define:TRACE --optimize+ --tailcalls+
-r:"C:\Program Files\FSharp-\CompactFramework\2.0\bin\FSharp.Core.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\Microsoft.Xna.Framework.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\mscorlib.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\System.Core.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\System.dll"
--target:library --warn:3 --warnaserror:76 --fullpaths --flaterrors
<your F# source files here>

Beside specifying the correct XNA assemblies using "-r:", this command line also does the following:

- Enables optimization using --optimize+ and --tailcalls+
- Embeds the F# core library in the assembly, using --standalone

Not very pretty, and hopefully future versions of F# and its integration into Visual Studio will remove the need to call fsc.exe explicitly.


Steve said...

Building against the XNA runtime looks a very similar problem to building against the Silverlight runtime as it was a year ago. You should be able do it via the Visual Studio integration, but there will be manual steps -- to wit, having created a default F# class library project, deleting all the supplied references and then manually navigating to all the ones listed here (and adding --standalone to the "Other flags" box on the project properties build tab.

Once you've fixed up all these bits, it would be possible to write a .vstemplate file for the otherwise empty project, zip the lot up and add it to the existing templates in C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ProjectTemplates\FSharp, so you never have to do that manual step again.

Joh. said...

Steve, that won't work. The problem is that Visual Studio overrides the programmer's choice of assemblies, and links against the PC's mscorlib and FSharp.Core, regardless of which versions of these assemblies the programmer actually wants to use.

Steve said...

The secret sauce is the --noframework directive,

--noframework Do not reference the default CLI assemblies by

Using this in a Visual Studio Silverlight project, happily builds against the explicitly specified Silverlight versions of mscorlib and FSharp.Core.

Confirmation arrives in the form of the a command line that looks like this in the VS Output window

C:\Program Files\FSharp-\\bin\fsc.exe -o:obj\Debug\astroclock-fs.dll -g --debug:full --noframework --define:DEBUG --define:TRACE --define:SILVERLIGHT --optimize- --tailcalls- -r:"C:\Program Files\fsharp-\Silverlight\2.0\bin\FSharp.Core.dll" -r:"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\mscorlib.dll" -r:"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\System.Core.dll" -r:"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\System.dll" -r:"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\System.Net.dll" -r:"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\System.Windows.Browser.dll" -r:"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\System.Windows.dll" -r:"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\System.Xml.dll" --target:library --warn:4 --warnaserror --warnaserror:76 --vserrors --utf8output --fullpaths --flaterrors Computation.fs Module1.fs AssemblyInfo.fs

As this yields a working Silverlight app, it has clearly bound against the restrictive platform framework. I would be very surprised if the XNA runtime is any different.

Steve said...

A quick followup with the real answer.
I got luck in last year's work I did with Silverlight as I only referenced the FSharp.Core and System.Core assemblies -- no explicit reference to mscorlib at all.

A quick bit of experimentation this morning shows that if you just navigate to the SIlverlight mscorlib, the .fsproj file simply gets

<Reference Include="mscorlib">

which is the main framework one. If you open the project file and edit it to give an explicit hint path

<Reference Include="mscorlib">

<HintPath>C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\mscorlib.dll</HintPath>


then it sticks. And having edited once, you can make yourself a reusable Visual Studio template.