dllhell

Porting Meander to Windows

Alternately titled: This is why I laugh when I hear people say that it’s hard to develop for Linux.

Warning! Extremely technical post! Skip to the bottom for the video.

Now that I’ve finished my GUI system I figured it was high time I get a Windows build done. Setting up a dev environment on Windows is about as painful as it gets, but it’s got to happen eventually. My journey begins!

I’ve already installed my IDE (Codeblocks with the custom Nuwen MinGW compiler distribution. I’ve already set up the SFML libraries, so I won’t be going into detail regarding those. The libraries giving me trouble are Lua and Luabind.

I downloaded a source package from my repository and attempted to compile the project without any changes. GCC throws up a few warnings I’ve never seen on Linux:

Meander\Meander\Actors.cpp|172|warning: converting to non-pointer type ‘int’ from NULL

But hey, they’re just warnings.

Then the errors start popping up.

Meander\Meander\Factory.cpp|28|error: invalid initialization of reference of type ‘const std::string&’ from expression of type ‘boost::filesystem3::path’
Meander\Meander\Factory.cpp|9|error: in passing argument 1 of ‘MN::Actor* MN::Factory::CreateActor(const std::string&)’

The version of Ubuntu I use (10.04) comes with release 1.40 of the Boost C++ libraries. Nuwen MinWG 7.1 comes bundled with 1.46, and it looks like the newer release isn’t backwards compatible. Ironically, the reason I went with Nuwen was because of the bundled libraries. Oh well, better add Boost to my list of libraries to compile. I’ll hit that one first.

Boost uses a build system called bjam, which is similar to cmake or autotools in that it doesn’t compile anything itself; rather it configures the build settings to whatever compiler toolchain you use and builds using that.

I copy bjam.exe into the boost source folder, open a command prompt and cd in.

C:\Users\Jake\Desktop> bjam –toolset=gcc

Of course g++ and gcc aren’t in my executable path. Silly Windows.

C:\Users\Jake\Desktop> set PATH=C:\Mingw\MinGW\bin\;%PATH%

C:\Users\Jake\Desktop> bjam –toolset=gcc

compiling…

5 minutes…
7 minutes…

According to the Boost website, compiling can take up to 20 minutes. While I’m on Windows I might as well play Half life 2.

After some fooling around in Ravenholm I check back on the build. It’s done! I copy the libs and headers over to my compiler installation, and reconfigure the linker settings in Codeblocks.

Meander compiles and links to boost_filesystem…and then this.

Meander\Gooey\WidgetManager.cpp|24|undefined reference to `lua_close’|
[…]
||=== Build finished: 50 errors, 0 warnings ===|

For now I’ll just comment out all the lines with Lua. It’s all contained within one class, so it won’t take too long. Recompile.

Another undefined reference to boost.system…time to figure out which of the five nearly identically named static libraries is the right one. Recompile.

Done! Still have those two warnings, but they’re not going anywhere. Time to run the game!

sigh…dlls again. Silly Windows. I copy every single .dll file from the SFML binary folder, just to be on the safe side.

Success! A window appears with graphics in! The menu buttons still don’t work because of the Lua dependancy, but that’s next on the list. In addition, my cursors aren’t changing when over buttons and textboxes, but that’s probably an easy fix.

Lua time!

Thanks to LuaBinaries, which I’d never heard of before, I was able to uncomment all the lua code and compile/link cleanly.
Clicking on a button still causes the program to freeze and stop working due to the fact that the button’s command hasn’t been bound to a function yet. Next up: Luabind!

My initial searches for variations of “luabind mingw static lib” don’t result in anything helpful. Eventually I created a CodeBlocks static library project and followed this guide, hoping that the changes between luabind 0.7 and 0.9 haven’t broken anything. Now to copy the headers and the library I just compiled to their respective folders, compile and run.

Unexpected success! GUI buttons now execute the correct actions, and all is well. The cursor still remains its ugly arrowish self no matter what element it is over, though. Time for some *shudder* WINAPI programming.

One of my goals in this project is that it compile on any platform without changes. In the case of system-specific code I make an exception, but only if the interface code is the same. So having #ifdef SFML_SYSTEM_WINDOWS in a class is fine as long as the member functions are identical accross operating systems. Ultimately the GUI::Cursor class should only be modified through the SetCursor(CursorType) function. Time to get my hands dirty.

Every time I see a piece of WinAPI code I thank God I’m not paid to program with it. It’s ugly as hell, and the names always make me feel like the guy who came up with them had a bad speech defect. The windowhandle type was inspired by the sound you make when you get punched in the stomach. HWN…D

But I’m a perfectionist, and right now I need to make sacrifices for the greater good.

The class I’ve been using to manage my cursors was originally posted over on the SFML wiki. Before I started porting this project to Windows it was about %70 new code, since the original was hideously buggy. I assumed that the windows-specific code was more or less in good shape, but I’d never tested it. Turns out that code was equally buggy. Sigh…

Greivances along the way:

The function LoadCursor() sets the cursor style, and the function SetCursor() applies it to the display. Why? One function should be enough, no?

Also, it seems that if you don’t handle the cursor in precisely the right place the cursor will flicker back and forth between what it should be and the default arrow.

But in the end I prevailed, and with any luck I’ll never have to touch that code again. Upside is it’s now been 100% rewritten.

Thanks to the cross-platform design of SFML I didn’t have to make any other changes. The program runs exactly the same as on Linux as long as I disable the console. (protip: printing to the console on Windows is incredibly expensive in terms of CPU/memory)

Aftermath of the project:

  • I learned enough about WINAPI to hack together a solution that I had solved elegantly and simply with xlib.
  • I compiled my first static library, which made me feel awesome.
  • I can once again say that Meander is cross-platform (except for Mac, but I don’t know anyone with a Mac).
  • Now that I’ve made visible progress I’ve put up another demonstration video! The link is here.

Edit: I probably should have mentioned that all this effort was neatly sidestepped on Linux by typing the above in a terminal window:

sudo apt-get install libboost-filesystem-dev liblua5.1-dev libluabind

I like doing things the simple way. :)