Tuesday, April 24, 2007
.Net, Mono, Gtk# and IronPython on Mac OS X
Today’s development platform test was on Mono and IronPython with Gtk#. I was testing the feasibility of achieving three goals:
- Support both Windows and Linux with a single codebase, using either Microsoft’s .Net Framework or Mono.
- Provide a smooth learning curve for my existing .Net dev team.
- Use Python for rapid development, without resulting in disjoint codebases.
My current requirement is for several hundred Windows desktops that I’d rather be using a more maintainable operating system on. I’m bound to Windows by third party software that I must support, but don’t want my team’s own work to become a barrier to migration in future.
Mono doesn’t support Windows.Forms very well, so if I am to adopt it, the first task will be to replace WinForms with Gtk# in existing apps. For the evaluation today, I tried the most basic: getting Mono and Gtk# working, and maybe writing a “Hello, World” Gtk# app in Python. My primary environment is a Mac, which made this evaluation more interesting. Could I actually do my development on OS X and deploy on Windows and Linux? The Mono distribution for Mac OS X includes IronPython but not Gtk# (Cocoa# is bundled instead). Gtk# requires the Gtk+ library, which is not a standard component on OS X, and hence Mono’s understandable omission.
Compiling Gtk# involved jumping a few hoops. Basically: symlink the Mono C# compiler /usr/bin/mcs to /usr/local/bin/csc.exe, because Gtk#’s configure script assumes a Microsoft .Net Framework environment, and then edit all Makefiles to set RUNTIME = mono. That compiled it. To install, I had to create gtk-sharp-2.0 under /Library/Frameworks/Mono.framework/Versions/1.2.1/lib/mono and throw in symlinks to all the DLLs from their source location in ../gac.
I then tried this simple Gtk# example in C#:
using Gtk; using System; class Hello { static void Main() { Application.Init (); Window window = new Window ("helloworld"); window.Show(); Application.Run (); } }
But it wouldn’t compile:
$ mcs HelloGtk.cs -pkg:gtk-sharp-2.0 Package gtk-sharp-2.0 was not found in the pkg-config search path. Perhaps you should add the directory containing `gtk-sharp-2.0.pc' to the PKG_CONFIG_PATH environment variable No package 'gtk-sharp-2.0' found error CS8027: Error running pkg-config. Check the above output.
Turned out there were two copies of pkg-config, one from Fink and one from Mono. The solution was to set a common environment variable:
export PKG_CONFIG_PATH=/sw/lib/pkgconfig\ :/usr/lib/pkgconfig\ :/Library/Frameworks/Mono.framework/Versions/1.2.1/lib/pkgconfig
The code compiled but still wouldn’t run:
$ mono HelloGtk.exe Unhandled Exception: System.DllNotFoundException: libgtk-x11-2.0.0.dylib at (wrapper managed-to-native) Gtk.Application:gtk_init (int&,intptr&) at Gtk.Application.Init () [0x00000] at Hello.Main () [0x00000]
Mono couldn’t find the Gtk+ libraries in /sw/lib. Fixing this was uglier; it required messing with the DYLD_LIBRARY_PATH environment variable:
$ export DYLD_LIBRARY_PATH=/sw/lib\ :/Library/Frameworks/Mono.framework/Versions/1.2.1/lib
HelloGtk.exe would run after this, but if I tried anything else, it failed complaining about missing libraries. I couldn’t find a solution. Anyway, I moved on Gtk# from Python, translating the code above line for line. It wouldn’t work. No known module named “Gtk”. Much head-scratching and Googling later, I learnt that IronPython needs a special invocation for .Net assemblies (that’s .Net speak for “libraries”). Here’s the code:
import clr clr.AddReference('gtk-sharp') import Gtk Gtk.Application.Init() window = Gtk.Window("helloworld") window.Show() Gtk.Application.Run()
Kushal has a slightly more elaborate example.