I have a little Arduino project for which I’m going to provide a nice graphical interface. As is my custom, I chose to implement everything using my Linux development environments but made it a point to choose powerful tools that are designed to be cross-platform. It’s a drag to do things over just because your targeting some other OS.
Though my code was done in a manner that would make compiling it on other platforms simple, getting everything setup to actually do it was a bit of an adventure in and of itself. Here I’ll go over the process and provide some hints as to how to get over the obstacles I had trouble surmounting.
I love the boost libraries for many reasons, but in this case my use was mainly on the smart pointer, regular expression and low level I/O fronts. Boost is great, and it takes the hassle out of all the above (and more) while retaining the ability to work on pretty much any flavour of *nix, Mac and Windows.
For the GUI I went with wxWidgets, an awesome GUI library that lets you code once and compile anywhere while using the native user interface on whichever platform you’re building for.
Once I had the program running under Linux, it was time to try out a build on another OS. I had an untouched Windows partition on a laptop so I booted up (Windows 7) for the first time in order to compile my program.
The obvious choice here, for me, was to get GCC running under windows–something I’d never attempted. Thankfully, there are a couple of projects available to make this easier, namely Cygwin and MinGW (Minimalist GNU for Windows).
Cygwin provides a number of tools to give anyone familiar with Linux a comfortable home under Windows. It includes all the basics (like bash and all your favourite command-line tools), a complete build system (automake, gcc, etc) and a huge number of optional add-ons.
MinGW, as the name implies, is a more minimalist solution but still provides a basic (and I mean basic) shell and what you need to get some compiling done.
I installed both using each of their simple GUI installer programs. For Cygwin, I installed all the dev tools along with the Boost libraries. For MinGW, I included the “MSYS” component which gives you a few additional tools and the standard home directory and such.
I didn’t find any mention of boost libs for MinGW. Because I never enjoy the process of getting boost built (it uses bjam or other weird tools and has a bunch of config methods and options) on any OS, and have never done it for Windows, I elected to give Cygwin the first shot.
If you’re familiar with any unix, opening the cygwin terminal will be immediately familiar. I unzipped the wxWidgets latest stable release (2.8.12) into my cygwin home (D:\cygwin\home\MyName\ in this case) and, from within the Cygwin bash could see the files under ~/wxWidgets-2.8.12/.
Once I ran configure and started the build, everything went along fine until I hit a snag around the 480th object file compile:
../src/generic/dirctrlg.cpp:67:24: direct.h: No such file or directory
Agh! The offending bit of code, from dirctlg.cpp was this:
#if !defined(__GNUWIN32__) || (defined(__MINGW32_MAJOR_VERSION) && __MINGW32_MAJOR_VERSION >= 1) #if !defined(__WXWINCE__) #include <direct.h> #endif #include <stdlib.h> #include <ctype.h> #endif
Apparently, the compiler hadn’t set an expected __GNUWIN32__ define and was attempting to locate direct.h — a mysterious file related to filesystem access of directories. I tried a number of solutions, like defining __GNUWIN32__ or commenting out the include, and search the web but it was all to no avail. I couldn’t seem to compile wxWidgets using the (then current) install of Cygwin.
I tried the latest development release, wxWidgets-2.9.4, and got the same result.
I tried another tack, by unpacking the wxWidgets source under the MinGW home (in my case, this was under D:\MinGW\msys\1.0\home\MyName) and then opening the MinGW terminal. From there, following the wxWidgets instructions for MinGW in INSTALL-MSW.txt resulted in a slow but hitch-less build.
Under MinGW, the build seemed to go at a snail’s pace, both because of the extra libraries required (image processing was using wxWidgets implementations of tiff/png/jpeg/etc libs) and because the compile steps seemed more labour-intensive (reasons unknown).
So the problem with wxWidgets was definitely Cygwin-related. I had it working under MinGW, though… but the thought of trying to get Boost built under the minimalist environment was so daunting, and I was getting so annoyed of dealing with the Windows UI, that I took a step back and breathed a sigh of relief as a booted back into Linux.
Windows is no fun: back to Linux
Surely there must be a way to build Windows executable using Linux! A little research (that I should have done from the get-go) indicated this should be possible. Using Ubuntu’s Synaptic package manager, I found mingw-64. MinGW-64 “provides a development and runtime environment for 32- and 64bit Windows applications using the GNU Compiler Collection (gcc)”. Mmmm, sounds nice.
I wound up with a couple of cross-compilers installed. The i686-w64-mingw32* variant (like i686-w64-mingw32-g++) is geared towards cross-compiling to 32-bit MinGW-w64 targets, while the x86_64-w64-mingw32 stuff (e.g. x86_64-w64-mingw32-g++) is for generating 64-bit versions of the same.
Feeling happy that I might get to build Windows executables under Linux, I was motivated to (once again) dive into the documentation for actually building Boost.
The first thing was to setup boost’s tools/build/v2/user-config.jam file. Assuming you want to build for 32-bit Windows (i.e. you’ll be using i686-w64-mingw32-g++), start be getting the version number by running
$ i686-w64-mingw32-g++ -v
For me the output includes:
Using built-in specs. COLLECT_GCC=i686-w64-mingw32-g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-w64-mingw32/4.6/lto-wrapper Target: i686-w64-mingw32 Thread model: win32 gcc version 4.6.3 (GCC)
So we’ll call the version “4.6”. Now, in the unpackaged boost source dir, edit tools/build/v2/user-config.jam and go to the “GCC Configuration” section and add:
# ------------------ # GCC configuration. # ------------------ # Configure specific gcc version, giving alternative name to use. using gcc : 4.6 : i686-w64-mingw32-g++ ;
Call the bootstraph shell program:
Finally, choose an install location for all your Windows stuff, so it won’t interfere with or overwrite anything you use under Linux. For this, I chose /usr/local/win32. Using this as the prefix, run bjam:
./bjam toolset=gcc target-os=windows variant=release \ --prefix=/usr/local/win32 \ threading=multi threadapi=win32 \ link=static runtime-link=static -j 2 \ --without-mpi --without-python \ -sNO_BZIP2=1 -sNO_ZLIB=1 \ --layout=tagged install
It will build everything and attempt to install. If you’re like me and not running this command as root, then when the build is done just re-run the same command as root (sudo ./bjam toolset=…) and everything will get installed under /usr/local/win32.
All this went rather well for me except that boost seems to have used the regular “ranlib” to index the installed libraries and this was causing issues on compile. The error was:
/usr/local/win32/lib/libboost_regex-mt-s.a: could not read symbols: Archive has no index; run ranlib to add one
If you get this too, you can try my solution which was simply to run the appropriate version of ranlib on the libraries produced:
$ cd /usr/local/win32/lib $ for i in libboost*; do sudo i686-w64-mingw32-ranlib $i; done
For wxWidgets, unpack the source code and cd into that directory. To build it, you’ll want to specify the “host” (i.e. the type of system you’re building for) as well as the prefix. From the wxWidgets source directory, run something like
$ mkdir mybuild $ cd mybuild $ ../configure --prefix=/usr/local/win32/ --host=i686-w64-mingw32 \ --disable-shared --disable-debug --enable-unicode --without-libtiff $ make; make install
I want to statically link everything so I ran the configure with –disable-shared. Also, I had some trouble with the TIFF library so I decided to just avoid the problem as I don’t use this image format anyway.
It took a while, but everything seemed to go well. A quick test using one of the samples:
$ cd samples/notebook $ make
built a little executable that ran successfully on my Windows 7 machine. Yay!
Building your own program
When it came time to build my own program, I encountered a few issues. The most important of which were unicode support and problems linking with the Boost thread library. A good deal of experimentation later, I finally managed to build everything using the following options.
For compilation, the commands look like
$ i686-w64-mingw32-g++ -c -o MyFile.o -DWIN32 -DUNICODE -D_UNICODE \ -DWX_PRECOMP -I/usr/local/win32/include \ `/usr/local/win32/bin/wx-config --cxxflags` -fno-strict-aliasing -municode \ MyFile.cpp
In the end, I’m not certain all the defines are actually needed but if it ain’t broke… The important things to note are:
* the inclusion of the prefix path include directory (for my boost install)
* the use of the appropriate wx-config to get the c++ flags and include paths for the cross-compiled wxWidgets
For linking, I use something like:
$ i686-w64-mingw32-g++ -o MyOutput.exe *.o -static \ `/usr/local/win32/bin/wx-config --libs` \ -L/usr/local/win32/lib -lboost_regex-mt-s -lboost_filesystem-mt-s \ -lboost_system-mt-s -lboost_thread_win32-mt-s \ -mthread
Again of note is the use of the cross-compile-generated wx-config, as well as the various boost library paths and references. If you’re getting the “could not read symbols: Archive has no index” error here, see above for the fix.
Finally, I was having a load of issues linking with the boost::thread library. For some reason, the linker was unable to locate a few methods including boost::thread::detach, boost::thread::get_id and others.
The errors looked like this:
../myLib/myLib.a(Util.o):Util.cpp:(.text$_ZN5boost6threadD1Ev[boost::thread::~thread()]+0x3c): undefined reference to `__imp___ZN5boost6thread6detachEv' ../myLib/myLib.a(Util.o):Util.cpp:(.text$_ZN5boost6thread4joinEv[boost::thread::join()]+0x43): undefined reference to `__imp___ZNK5boost6thread6get_idEv' ../myLib/myLib.a(Util.o):Util.cpp:(.text$_ZN5boost6thread4joinEv[boost::thread::join()]+0x5a): undefined reference to `__imp___ZN5boost11this_thread6get_idEv' ../myLib/myLib.a(Util.o):Util.cpp:(.text$_ZN5boost6thread4joinEv[boost::thread::join()]+0xad): undefined reference to `__imp___ZN5boost6thread13join_noexceptEv' collect2: ld returned 1 exit status make: *** [MyProg] Error 1
If you are trying to use boost threads and are encountering similar errors, add this prior to your include of the boost thread header:
#ifndef BOOST_THREAD_USE_LIB // required for mingw32 linking of thread libs #define BOOST_THREAD_USE_LIB #endif #include <boost/thread.hpp>
Recompile everything related to threads (ideally just make clean and rebuild), and you should be good to go.
“Loss of precision” errors
On a final note, during the compilation for wxWidgets–I think, hard to recall by this point and it might have been boost–I was getting a ton of errors like:
error: cast from 'foo' to 'bar' loses precision [-fpermissive]
This could be sidestepped by issuing the same compile command and including the -fpermissive flag. But that was a pain, and there were a whole lot. I was getting tired of all the messing around, so I cheated a bit and did this:
$ sudo mv /usr/bin/i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-standard
and then created a shell script in place of /usr/bin/i686-w64-mingw32-g++ which contains:
!/bin/sh /usr/bin/i686-w64-mingw32-g++-standard -fpermissive $@
to include the flag on all compilation commands. Make sure that shell script is executable with
$ sudo chmod a+x /usr/bin/i686-w64-mingw32-g++
and issue another make to get rolling.
It was a bit of an ordeal finding a way to get everything working, but now I’m a happy pappy developing under Linux for both Linux and Windows users. Hopefully, using the above will help a few people get a similar setup going with a lot less trouble. Enjoy!