There are many Open-Source projects out there, and many of them can be built for MS-Windows. There are actually very few people that does so, because configuring the development environment (MinGW) in Windows is a pain. And upgrading it to the latest version is ever worse.
So I had the idea of not even trying. Use Windows as an embedded system, and cross-compile everything from a Linux box!
For illustration purposes (and because that's what I need just now), I will build the Poppler package for Windows.
If you don't want to read through all this and prefer an automatic that does the hard work for you, skip to the last section.
For this you will need an up-to-date Linux system. I'm using Arch Linux, but I've also tested it in an Ubuntu-14, and it works mostly the same. You have to install the MinGW cross compiler. Fortunately, these days, many Linux distributions package the MinGW-w64 version, that works really, really well.
In Arch Linux the main package is called
mingw-w64-gcc. In Ubuntu and other
Debian distros, installing the package
g++-mingw-w64 will kick in all the
A Windows machine (or VM) will be handy for testing but it is not strictly required. For a quick and dirty test you can even use Wine!
Any non trivial OS project depends on many more OS projects, so you have to build all the dependencies before you can go on and build your final target. Many of these dependencies are optional: you can omit them, but then you will have less functionality, it is up to you to decide whether you want them or not.
Also note that there may be dependencies on specific versions of other projects (not older than X, but not newer than Y...). And also remember that the Windows versions of many of these packages are not so well tested as in other more popular systems. In fact many of them are only thoroughly tested when they are built as part of a major project, such as The Gimp or Inkscape.
I've decided to delegate the version checking to my Linux distribution. That is, I'll build the same version of all the packages I have installed right now. After all, somebody already checked that they work well together. About the Windows specific bugs… well, I'll just take my chances.
Ok, then let's check the dependency tree of Poppler. My Linux have
poppler-0.26.3, that depends on
lcms2. Of these,
gcc-libs is provided by MinGW, so nothing to
fontconfig is used to manage the installed fonts, but in Windows
Poppler is able to use the native Win32 font API so it is not actually needed;
lcms2 is used to advanced color management, but I will only use Poppler
for black&white documents; and
openjpeg is used for the not so widely used
JPEG 2000 codec, that I don't need.
poppler I'll have to build
cairo. The first one is
easy, no further dependencies, I'll use
cairo is a big library, with a lot of dependencies. Fortunately, most of
them are optional. I'll just take
pixman is a library to manage pixel arrays. No dependencies.
libpng is the library to read PNG images. It depends on
freetype2 is the library that understand TrueType font files. No further
zlib has no more dependencies.
So are we done with the dependencies? Not yet. I'll also build
translation support and
libiconv for character encoding conversion. The latter
one is available by default in any POSIX system, but it is not present in MinGW,
so they may assume that it is available. These two packages, particularly
gettext are usually optional, so you can skip them if you think you don't need
Finally I will also build
gdbserver to be able to debug remotely.
Summing up, these are the packages to be built, in the proper order:
- gdbserver 7.8
- libiconv 1.14
- gettext 0.18.3.2
- zlib 1.2.8
- libpng 1.6.10
- libjpeg 1.3.1
- freetype 2.5.3
- pixman 0.32.6
- cairo 1.12.16
- poppler 0.26.3
Preparing the environment
First of all you have to create a directory where to install everything. I will
WINPREFIX. It will be a bit like the Linux
if you prefer, as the sys-root of an embedded device. Just ensure you have
write permissions. But please, do not use spaces in this name.
$ export WINPREFIX=$HOME/mingw
Then, you have to determine the host triplet, that is, the name of the target
system. It will be the prefix of the MinGW compiler. That is, if the installed C
$ export HOST=i686-w64-mingw32
It is also a good idea to specify the build triplet, that is the identification of the build machine. If you don't do this, some weird things may happen. If you don't know what your triplet is, you can use config.guess to find out:
$ export BUILD=x86_64-unknown-linux-gnu
Before we start compiling, we have to tell everyone where the include and library files will be located:
$ export CPPFLAGS="-I$WINPREFIX/include" $ export LDFLAGS="-L$WINPREFIX/lib"
And also tell
pkg-config where the installed packages are (some people build a
targeted version of
pkg-config but I find the native program perfectly
$ export PKG_CONFIG_LIBDIR=$WINPREFIX/lib/pkgconfig
cd into a working directory and we'll start building.
There are two kinds of packages: those that use
autoconf, and those that do not.
If a packages uses
autoconf, cross-compiling is just a matter of running
./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX plus whatever
additional options you fancy,
If it does not use
autoconf, then you need to read the documentation, be
creative and do a lot of trial and error.
Get gdb-7.8 and untar. We are only
interested in the
gdbserver because the Linux
gdb can do cross-debugging
just fine. And it uses
$ cd gdb-7.8/gdb/gdbserver $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX $ make $ make install
and untar. It uses
autoconf, too. Easy:
$ cd libiconv-1.14 $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX $ make $ make install
Get gettext-0.18.3.2 and untar. This package contains tools and libraries. The tools are not needed, as you can use the native ones, so we'll only build the libraries:
$ cd gettext-0.18.3.2/gettext-runtime $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX $ make $ make install
Get zlib-1.2.8 and untar. This library
does not use
autoconf, so getting it to work is tricky. Fortunately, it
Makefile just for MinGW that, with a bit of tweaking, works:
$ cd zlib-1.2.8 $ make -fwin32/Makefile.gcc PREFIX=$HOST- INCLUDE_PATH=$WINPREFIX/include \ BINARY_PATH=$WINPREFIX/bin LIBRARY_PATH=$WINPREFIX/lib \ SHARED_MODE=1 $ make -fwin32/Makefile.gcc PREFIX=$HOST- INCLUDE_PATH=$WINPREFIX/include \ BINARY_PATH=$WINPREFIX/bin LIBRARY_PATH=$WINPREFIX/lib \ SHARED_MODE=1 install
Get [libpng-1.6.10] and untar. Nothing to see here.
$ cd libpng-1.6.10 $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX $ make $ make install
Get libjpeg-turbo-1.3.1 and untar.
This library has a couple of issues. First, it uses NASM to compile some
hand-written optimized assembler. If you feel it is worth it, you can install
nasm package in your Linux box.. If you prefer to skip the
--with-simd=no to the
Second, there is a
typedef in a library header that clashes with another one in a
system header. It actually should be detected and corrected by
it is not. The solution is to add a couple of lines to the
jconfig.h file to
remove the clash.
$ cd libjpeg-turbo-1.3.1 $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX --with-simd=no $ cat >> jconfig.h << EOF /* RPCNDR.H defines boolean as unsigned char */ typedef unsigned char boolean; #define HAVE_BOOLEAN EOF $ make $ make install
and untar. This library has a lot of options to choose from, but instead of
giving them as options to
./configure, they are selected by changing a couple
of files. I'm not totally sure what they are for, so I'm doing the same patching
as my Arch Linux package.
--- i/modules.cfg +++ w/modules.cfg @@ -110,7 +110,7 @@ AUX_MODULES += cache # TrueType GX/AAT table validation. Needs ftgxval.c below. -# AUX_MODULES += gxvalid +AUX_MODULES += gxvalid # Support for streams compressed with gzip (files with suffix .gz). # @@ -129,7 +129,7 @@ # OpenType table validation. Needs ftotval.c below. # -# AUX_MODULES += otvalid +AUX_MODULES += otvalid # Auxiliary PostScript driver component to share common code. # --- i/include/config/ftoption.h +++ w/include/config/ftoption.h @@ -92,7 +92,7 @@ FT_BEGIN_HEADER /* This is done to allow FreeType clients to run unmodified, forcing */ /* them to display normal gray-level anti-aliased glyphs. */ /* */ -/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ +#define FT_CONFIG_OPTION_SUBPIXEL_RENDERING /*************************************************************************/ @@ -604,7 +604,7 @@ FT_BEGIN_HEADER /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ /* defined. */ /* */ -/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#define TT_CONFIG_OPTION_SUBPIXEL_HINTING /*************************************************************************/
Other than that, everything is as usual:
$ cd freetype-2.5.3 $ patch -p1 < ft.patch # the patch from avobe $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX $ make $ make install
Get pixman-0.32.6 and untar. You already know how to do it:
$ cd pixman-0.32.6 $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX $ make $ make install
Get cairo-1.12.16 and untar.
Cairo has a lot of options, backends, frontends, etc. Fortunately most of the options are already disabled by simply not having compiled the required libraries. You can even use Poppler to build a PDF backend (a circular dependency will require you to build these packages twice).
I'm ok with the autoconfigured defaults… except that it detects the native
Xlib libraries as available. Probably a bug in the
configure.ac file, but
nothing to worry about: just tell it not to use that library.
Also, by default, cairo will build the library and the test cases. I'm not really interested in the tests, mainly because they have additional requirements, so I'll skip those.
There is bug in the Win32 printer backend that prevents cairo from working at all with a printer. It took me a while but I managed to write a patch for it. I've already reported it upstream so maybe future version will be right. If you want to patch it, grab the patch from the linked bug report and apply it.
$ cd cairo-1.12.16 $ patch -p1 < cairo-win32-print.patch $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX --disable-xlib $ cd src # build only libcairo $ make $ make install
Get poppler-0.26.3 and untar.
Finally, we are able to build
$ cd poppler-0.26.3 $ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX $ make $ make install
I've written a bash script that builds all these packages automatically, and it also makes easy to add new packages or upgrade the existing ones.
Get it here. It is mingw-ceb (CEB is for Cross-Compilation Environment Builder). Please, be aware that I'm not very good writing bash scripts, it just gets the work done.
To use it, first make it executable and then run it. It takes two parameters. The first one is the host triplet. The second one is the installation directory.
$ chmod a+x mingw-ceb $ ./mingw-ceb i686-w64-mingw32 ~/mingw
The script remembers the already built packages. To reset them just remove the
src/build directory. For example, to rebuild the 64-bit version of everything:
$ rm -rf src/build $ ./mingw-ceb x86_64-w64-mingw32 ~/mingw64
You've succesfully built all the packages! Now what? Well, there are still a few
DLLs needed that are not in the destination directory, you may want to link or
copy them now from whatever your distribution puts them (
Also, if you plan on distributing these files you may want to strip them, as more than half their size is for debugging symbols only.
$ $HOST-strip -s $WINPREFIX/bin/*