The (initial) problemEveryone who's been developing games using XNA for the Xbox360, WP7 and the PC platforms knows managing projects and solutions is a bit troublesome. Here is the problem: in theory, all you would need is to have a single solution with three platforms: x86, xbox and wp7.
However, that's not how it works in practice. The various DLLs that you need to reference vary from one platform to the next, meaning you have to create multiple projects for each library. The XNA plugin for Visual Studio helps with the task of managing multiple libraries, but it doesn't work for F# projects.
I have developed a script that generates xbox projects from pc projects, but it's more of a hack than a reliable method. It also requires some amount of manual intervention. Although I am personally relatively satisfied with this solution, I imagine it will be close to no use to anyone but me.
The (supposed) solution
The release of Visual Studio 11 beta has exposed a new type of project: Portable Class Library. Here is what MSDN has to say about them:
Using a Portable Class Library project, you can build portable assemblies that work without modification in .NET Framework, Metro style, Silverlight, Windows Phone 7, and Xbox 360 apps.That sure sounds interesting. I decided to try this new feature by adopting it for XNAUtils.
The reality (more problems)
No need to maintain the suspense to the end of this post, I can already reveal I'm not positive about these libraries. The rest of this post describes a number of hurdles I have encountered so far.
The screenshots on MSDN don't look like my screen
The MSDN docs state that you can specify which platforms you intend to support. Since each platform has its limitations, limiting support to a number of platforms my allow for larger accessible feature sets.
That's how it's supposed to look. The project properties should have a button "Change" allowing to access the various platforms.
Sadly, that does not apply to F# prjects:
No "Change" button to be seen. Somewhat worryingly, the Xbox platform is not mentioned here.
In any case, I chose to ignore that problem and go ahead...
What kind of libraries can I refer to?
I will need to access at Microsoft.Xna.Framework, and possibly also Graphics. However, those are not available as portable libraries. I referenced the ones for Xbox, and hoped for the best. It might work on the xbox, but I don't see how that could possibly work on the PC?! I haven't come to the point where I could run something, so we'll see how that goes...
Dude, where's my Thread constructor?
One of my functions creates a thread. This code no longer compiles, I have posted a question on stackoverflow on the subject.
Summary: Apparently, the constructor I want to use is not available in portable class libraries, despite the MSDN doc claiming the contrary.
That's not very reassuring. Deciding whether to take the step to PCLs requires some thinking ahead, and the information available from official sources isn't reliable. I know it's still a beta release, but it's unpleasant nevertheless.
There is a feeling of deja-vu with this one. Back in the days when I was working on my asteroids clone, I had a problem with the thread class. I had used the PC dlls of XNA in my xbox build. That worked fine before XNA 3.1, but there was a catch. The code would compile, but crash when run on the xbox. The offender was a method of Thread that was not available on the xbox.
The solution consisted of wrapping the correct method in a delegate in the top-level C# code, and send it down to my F# code, which could invoke it to perform the required operation.
It seems I will have to use the same trick here if I want to be able to create threads. The portable framework doesn't allow creating threads, but the XNA framework, which "implements" the portable framework, has the constructor I need. A solution would therefore be to get the thread constructor from the top-level app (which is aware of the specific platform it will run on) and pass it down to the portable code.
This solution should be usable for any "non-portable" functionality that you know exists on the platforms you target.
The experiment is not conclusive yet, as I haven't gotten to the point where I can run things. I wonder if it's worth the trouble.
This shows linking to implementations is a mistake. What you should link to is signatures. Then it's up to the top level, the application, to specify a set of implementations which satisfy these signatures. Libraries have no business depending on implementations.
The approach consisting of providing implementations that "work everywhere" is probably not going to work well, since they are very likely to lack basic functionality that isn't available everywhere in exactly the same way.