Compile for Windows on Linux

Introduction

In an earlier post, we saw how to obtain gcc on windows, using the MinGW-w64 suite. However, users familiar to gcc are often using one of the operating systems of the Unix family, such as Linux. As amazing as it may first seem, the MinGW-w64 project allows users to compile native Windows binaries on Linux. This concept of targeting a different platform than the compiler is running on is however not new, and is known as cross-compilation.

Cross-compiling Windows binaries on Linux may have many benefits to it.

  • Increased compilation speed. Linux is generally faster than Windows with the mingw toolchain.
  • Reduced operating system complexity. On cross-platform projects that are also built on Linux, we can get one less operating system to maintain.
  • Access to Unix build tools. Build tools such as make, autoconf, automake and Unix utilities as grep, sed, and cat, to mention a few, become available for use in Windows builds as well. Even though projects such as MSYS port a few of these utilities to Windows, the performance is generally lower, and the versions are older and less supported than the native Unix counterparts. Also, if you already have a build environment set up under Linux, you don’t have to set it up again on Windows, but just use the existing one.
  • Lower license costs. As we know, Windows costs in terms of license fees. Building on Linux, developers do not need to have  a Windows installation on their machines, but maybe just a central Windows installation for testing purposes.

How It Works

On a Linux build environment, a gcc that compiles native binaries is usually installed in “/usr/bin”. Native headers and libraries are in turn found in “/usr/include” and “/usr/lib”, respectively. We can see that all these directories are rooted in “/usr”.

Any number of cross-compiler environments can be installed on the same system, as long as they are rooted in different directories. In our example, we will use “/opt/mingw32″ and “/opt/mingw64″ as root directories for the new build environments. Now, we would perhaps expect to find “/opt/mingw32/bin/gcc” and “/opt/mingw64/bin/gcc”, but we instead see “/opt/mingw32/bin/i686-w64-mingw32-gcc” and “/opt/mingw64/bin/x86_64-w64-mingw32-gcc”. The reason for this is that we  (and configure scripts) should be able to pick the “right” gcc, even if  we have multiple compilers in the PATH environment variable. If they were all named gcc, cross-compiling would easily become messy.

Cross-World Hello

  1. Go to the MinGW-w64 download page. We need two toolchains – one for targeting win32 and another for targeting win64. New packages are frequently uploaded, but we just pick the newest personal  build (I had some bad experiences with the automated builds). Open “Toolchains targetting Win32″ and “Toolchains targetting Win64″, followed by “Personal Builds”. At the time of writing, we can open for example “sezero_20110510″, and pick “mingw-w32-bin_x86_64-linux_20110510_sezero.tar.gz” and “mingw-w64-bin_x86_64-linux_20110510_sezero.tar.gz” to compile from a 64-bit Linux distribution. There are some notes on the package naming convention below to help you pick the right one.
  2. Unpack the first archive to /opt/mingw32 and the second to /opt/mingw64.
  3. In a text editor (e.g. gedit or nano), paste in the little greeting-code and save it to /tmp/hello.c

    #include <stdio.h>

    int main()
    {
    printf(“Hello World!\n”);
    return 0;
    }

  4. Compile it for both 32- and 64-bit Windows with the following commands.
    /opt/mingw32/bin/i686-w64-mingw32-gcc /tmp/hello.c -o /tmp/hello-w32.exe
    /opt/mingw64/bin/x86_64-w64-mingw32-gcc /tmp/hello.c -o /tmp/hello-w64.exe
  5. Run “hello-w32.exe” on 32-bit Windows, and “hello-w64.exe” on 64-bit Windows.

 

In order to build useful applications, it is convenient to use existing libraries such as the OpenSSL library on Windows.

Package Naming Conventions

As we saw on the MinGW-w64 download page, there are a lot of available packages with only subtle and perhaps confusing name differences. The automatically built packages have the following generic naming pattern.

mingw-TARGET-bin_HOST_DATE.PKG

  • TARGET states which platform we want the compiled binaries to run, and can be either “w32″ (32-bit Windows) or “w64″ (64-bit Windows).
  • HOST gives the host system, that is, the system on which the compiler binaries themselves are run. Thus, we are cross-compiling if HOST is different from TARGET. If we have a Intel 32-bit Linux distribution, we can pick a HOST value of “i686-linux”, from a 64-bit Linux host we would choose “x86_64-linux”, and from 32-bit Windows we can choose “i686-mingw”.
  • DATE is the date, in the form YYYYMMDD, when the automatic build was created.
  • PKG is the compressed archive format, such as “zip”, “tar.bz2″ and such. Generally, zip archives contain binaries that run on Windows, all other archives contains binarie that run on Linux.

Running the Binaries

Using Wine, we can even test the binaries directly from Linux. However, this only works on 32-bit Windows binaries and is not perfect due to bugs and missing features in Wine itself. After downloading and installing Wine for our distribution, we can test our program above by running “wine hello-w32.exe”.
Note that 64-bit Windows can run 32-bit binaries due to an emulation layer called Windows 32-bit On Windows 64-bit, but native binaries are more efficient.

This entry was posted in GCC Windows and tagged , , , , , , , , , . Bookmark the permalink.

23 Responses to Compile for Windows on Linux

  1. Pingback: gcc for Windows | Techblog

  2. Pingback: OpenSSL for Windows | BlogCompiler

  3. Divine says:

    I downloaded the package mingw-w32-bin_x86_64-linux_20110510_sezero.tar.gz
    and with the help of the following commands, i extrated and install the package.
    commands
    1.su
    2.tar xvfz mingw-w32-bin_x86_64-linux_20110510_sezero.tar.gz -C /opt

    I further wrote a temporal c program saved to /tmp/hello.c
    On trying to compile using the command below,

    /opt/mingw32/bin/i686-w64-mingw32-gcc /tmp/hello.c -o /tmp/hello-w32.exe

    I get report that binary file cannot be excuted.When I try to remove bin from the path ,the report is no such file or directory.
    Please help me.

  4. Divine says:

    I downloaded the package mingw-w32-bin_x86_64-linux_20110510_sezero.tar.gz
    and with the help of the following commands, i extrated and install the package.
    A:commands
    1.su
    2.tar xvfz mingw-w32-bin_x86_64-linux_20110510_sezero.tar.gz -C /opt

    I further wrote a temporal c program saved to /tmp/hello.c
    On trying to compile using the command below,

    /opt/mingw32/bin/i686-w64-mingw32-gcc /tmp/hello.c -o /tmp/hello-w32.exe

    I get the report that binary file cannot be excuted.When I try to remove bin from the path ,the report is no such file or directory.
    Please any help will be greatly appreciated.

    • author says:

      Hi,

      Are you using a 64-bit Linux distribution?
      You can check this with

      $ uname -m
      x86_64

      The above would indicate a 64-bit distribution, which is required for running the gcc command I linked directly to. If you are on a 32-bit distribution, please try packages that have “i686-linux” instead of “x86_64-linux” in the middle of their name.

      For example, try
      mingw-w32-bin_i686-linux_20110510_sezero.tar.gz.

      Also see the Package Naming Conventions above, especially the HOST part of it.

      Hope this helps!

  5. Keinstein says:

    Hi,

    if you have an apropriate windows installation and binfmt support installed you can configure any autoconf based project to use mingw if you add the following two parameters to configure:

    –host=i686-w64-mingw32 –build=i686-w64-mingw32

    for 64bit use x86_64-w64-mingw32. Thus you can convince configure that you are not on linux. It will fail if you don’t set up binfmt support (on debian just install wine and binfmt-support) as it concludes that the executables are broken if they cannot be run.

  6. Anu says:

    Hi,
    compiler works great! But these packages don’t contain an rc compiler, which is available in standard MinGW. Any help on how to get an rc compiler to work would be greatly appreciated.

    TIA

    • author says:

      Hi Anu,

      I have just used the rc compiler to generate event-logging dll’s. I then used the mc and rc compilers from Windows SDK and link from Visual Studio (natively from Windows). The event definitions almost never change, so this worked quite well for me.

      What I did was this:

      mc myevents.mc
      rc myevents.rc
      link -dll -noentry myevents.res /MACHINE:X86 # or X64

      This was probably not an answer to your question, but perhaps it helped a bit.

    • author says:

      Anu,

      Did you try the windres command (e.g. /opt/mingw64/bin/x86_64-w64-mingw32-windres)?
      This seems to be doing what you are looking for. It is described briefly on the “standard” MinGW web pages: http://www.mingw.org/wiki/MS_resource_compiler

      Thanks.

  7. Anu says:

    Hi,

    I tried to build a rubenvb 4.6.3-1 on Linux. First, the linker said:
    ld: unrecognised emulation mode: i386pe
    when trying to link the g++ binary. I found that it gets the idea to use a Windows mode from the gcc/gcc/specs file which is created from the newly built xgcc. So i removed ${SPECS} target from the makefile. Now it links nicely, but when I try to run the resulting binaries, I get:


    $ i686-w64-mingw32-g++ --version
    i686-w64-mingw32-g++: fatal error: switch 'no' does not start with '-'
    compilation terminated.

    The compiler I am using is gcc-4.6.3 and binutils 2.22. GnuMake is 3.81. System is x86_64 GNU/Linux.

    Any idea? Many thanks in advance!

  8. Jon says:

    /opt/W32_180676/bin/../libexec/gcc/i686-w64-mingw32/4.5.4/cc1: error while loading shared libraries: libmpfr.so.1: cannot open shared object file: No such file or directory

    I only find a libmpfr.so.4 in my system.

    • Jon says:

      Sorry for wasting your time. I `ldd’ and ‘ln’ until I got all libraries needed by the MinGW-64 gcc.

      • author says:

        No worries Jon, glad that you got it working!

      • guy says:

        I have the same issue, can you detail how you resolved this? Thanks.

        • guy says:

          where did you create the symbolic links to?

          • Guy says:

            The toolchains expect to find these libraries in their paths:
            gmp library
            mpfr library
            mpc library
            I only got complaints about the first 2 missing on Ubuntu 12.04.
            Ubuntu happens to have later major versions installed, symbolically link the mismatched library versions
            and hope for library API backwards compatibility. Otherwise you’ll have to install the exact versions (use ldd on the binary to find the dependency version as suggested).
            i.e. linking these versions has worked for me:
            ln -s /usr/lib/x86_64-linux-gnu/libmpfr.so.4 /usr/lib/x86_64-linux-gnu/libmpfr.so.1
            ln -s /usr/lib/x86_64-linux-gnu/libgmp.so.10 /usr/lib/x86_64-linux-gnu/libgmp.so.3

  9. Mike C says:

    “Linux is generally faster than Windows”

    Uh, not really. This may be true for very old Windows versions. Modern versions of the Windows kernel are extremely fast. One can be somewhat faster than the other sometimes depending on the hardware in the machine running, but generally speaking there will not be any substantial performance discrepancies. You should set up a test machine with Windows 7 and Linux dual boot, and compile something on both to benchmark the performance, and you will see that it’s about the same speed.

    • author says:

      Hi Mike,

      Thanks for pointing this out. My observation was that a mingw compilation run on Linux was several times faster than if you ran it on Windows.
      This was done on Windows XP, and I have indeed not tested the newer Windows versions.
      I will adjust the claim to limit it to mingw. I think you would agree then?

  10. Pingback: Working on Linux to compile for Windows

  11. Zee says:

    As far as I use simple hello world c program it works perfectly fine and generates a proper exe file for Windows 64 bit.But when I use some code which requires lib and include files to be added in the command then it generated the following error.
    If I use simple gcc command this works perfectly fine.
    ==================================================
    Command Executed.
    myroot@ubuntu:/home/mysystem/Desktop# /opt/mingw64/bin/x86_64-w64-mingw32-gcc /home/mysystem/Desktop/hello.c -I /home/mysystem/Desktop/oracle/include/ -L /opt/Db//lib/ -L /opt/Db/9/lib -ldld -o /home/zainabmasood/mystem/testme.exe
    ===================================================
    Error :(
    /opt/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.5.4/../../../../x86_64-w64-mingw32/bin/ld: cannot find -ldld
    collect2: ld returned 1 exit status
    ===================================================
    Need Help Plz.

  12. Chris says:

    Thanks for this, I’ve got a simple foo.c compiling to foo.exe and running on windows.

    How would you go about compiling a windows executable that will depend on a library (libSDL2 in my case)?

    If I try:
    /opt/mingw64/bin/x86_64-w64-mingw32-gcc foo.c -o win64/foo.exe -lSDL2
    I get:
    foo.c:2:22: fatal error: SDL2/SDL.h: No such file or directory

    The linux version works (and runs):
    gcc foo.c -o linux64/foo -lSDL2

    Do I need to get the headers from a (pre-compiled) windows version of the library?

    Where should I put said headers? (i.e. where does /opt/mingw64/bin/x86_64-w64-mingw32-gcc look for headers, because it’s not getting the linux SDL2/SDL.h (which probably wouldn’t work anyway).)

    Can you compile a windows executable on linux without the windows DLL libraries being present on the linux box?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>