11 de Agosto de 2014
Labels: compilador, gcc, mingw.

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!

The goal

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.

Prerequisites

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 necessary dependencies.

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!

Dependencies

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 libjpeg, gcc-libs, cairo, fontconfig, openjpeg and lcms2. Of these, gcc-libs is provided by MinGW, so nothing to do here; 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.

So, before poppler I'll have to build libjpeg and cairo. The first one is easy, no further dependencies, I'll use libjpeg-turbo.

But cairo is a big library, with a lot of dependencies. Fortunately, most of them are optional. I'll just take libpng, freetype2 and pixman.

pixman is a library to manage pixel arrays. No dependencies.

libpng is the library to read PNG images. It depends on zlib.

freetype2 is the library that understand TrueType font files. No further dependencies.

zlib has no more dependencies.

So are we done with the dependencies? Not yet. I'll also build gettext for 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 them.

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 call it WINPREFIX. It will be a bit like the Linux /usr or /usr/local, or 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 compiler is i686-w64-mingw32-gcc:

$ 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 capable):

$ export PKG_CONFIG_LIBDIR=$WINPREFIX/lib/pkgconfig

Now, cd into a working directory and we'll start building.

Building packages

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, make and make install.

If it does not use autoconf, then you need to read the documentation, be creative and do a lot of trial and error.

gdbserver

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 autoconf:

$ cd gdb-7.8/gdb/gdbserver
$ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX
$ make
$ make install

libiconv

Get libiconv-1.14 and untar. It uses autoconf, too. Easy:

$ cd libiconv-1.14
$ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX
$ make
$ make install

gettext

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

zlib

Get zlib-1.2.8 and untar. This library does not use autoconf, so getting it to work is tricky. Fortunately, it includes a 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

libpng

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

libjpeg

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 the nasm package in your Linux box.. If you prefer to skip the assembly, add --with-simd=no to the ./configure call.

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 ./configure but 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

freetype

Get freetype-2.5.3 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

pixman

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

cairo

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

poppler

Get poppler-0.26.3 and untar.

Finally, we are able to build poppler:

$ cd poppler-0.26.3
$ ./configure --host=$HOST --build=$BUILD --prefix=$WINPREFIX
$ make
$ make install

Automatize it

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

After building

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 (libstdc++-6.dll, libwinpthread-1.dll, libgcc_s_sjlj-1.dll…).

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/*

1 comment to Build a MinGW cross-compiler environment

  1. More easy with MSYS2...

Help
:-(icon_sad :-)icon_smile :roll:icon_rolleyes
:-Dicon_biggrin :-Picon_razz :oops:icon_redface
:-xicon_mad :-|icon_neutral :arrow:icon_arrow
8-)icon_cool 8-Oicon_eek :mrgreen:icon_mrgreen
:!:icon_exclaim :?:icon_question :twisted:icon_twisted
;-)icon_wink :evil:icon_evil :idea:icon_idea
:-oicon_surprised :cry:icon_cry
:-?icon_confused :lol:icon_lol

Leave a comment