GTK3-cross-compile

Cross-compile a GTK3 app from Linux to Windows
git clone git://git.wrycode.com/wrycode/GTK3-cross-compile.git
Log | Files | Refs | README

README (12520B)


      1 	   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      2 	     CROSS-COMPILE A GTK3 APP FROM LINUX TO WINDOWS
      3 		   WITH NATIVE WINDOWS LOOK AND FEEL
      4 	   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      5 
      6 
      7 One of my research projects involves writing a Windows app for a
      8 department at my school, but I only run Linux. I saved the steps for
      9 building a GUI [GTK3] program on both Linux and Windows. This includes
     10 the frills most Windows users expect, like a graphical installer, an
     11 icon on the executable, and a program that looks like it was made for
     12 Windows.
     13 
     14 As a starter program, create the demo C program from [the GTK
     15 documentation] and save it as `main.c'.
     16 
     17 To compile the code on Linux, a simple
     18 ┌────
     19 │ $ gcc -o main main.c `pkg-config --libs gtk+-3.0 --cflags gtk+-3.0`
     20 └────
     21 will do the trick (you need pkg-config, GCC, and GTK3 installed). Test
     22 the executable to make sure it works:
     23 ┌────
     24 │ $ ./main
     25 └────
     26 
     27 
     28 [GTK3] <https://www.gtk.org/>
     29 
     30 [the GTK documentation]
     31 <https://developer.gnome.org/gtk3/stable/gtk-getting-started.html>
     32 
     33 
     34 Windows setup
     35 ═════════════
     36 
     37   To build for Windows, you need to use [MinGW] to compile the
     38   executable, and then you need to bundle the MinGW GTK dlls so they’re
     39   available at runtime on Windows.
     40 
     41   First install MinGW. If this step seems insurmountable, or if you’re
     42   running Arch Linux or a similar distro, just skip to the [Automating
     43   builds] section.
     44 
     45   On Fedora, for example, you can install everything you need with this
     46   command:
     47 
     48   ┌────
     49   │ dnf install mingw64-gtk3 mingw32-binutils mingw32-nsiswrapper
     50   └────
     51 
     52 
     53 [MinGW] <http://www.mingw.org/>
     54 
     55 [Automating builds] See section Automating builds
     56 
     57 Compiling for Windows
     58 ─────────────────────
     59 
     60   On a Linux system with MinGW installed you can compile the same
     61   program with something like:
     62   ┌────
     63   │ $ x86_64-w64-mingw32-gcc -o main main.c `mingw64-pkg-config --cflags gtk+-3.0 --libs gtk+-3.0`
     64   └────
     65 
     66   You should now have an executable called `main.exe'. Obviously this
     67   executable won’t work on Linux, but it also won’t work on Windows
     68   until you make the GTK libraries available to it at runtime.
     69 
     70 
     71 Bundle GTK libraries
     72 ────────────────────
     73 
     74   Make a subdirectory called `windows' and copy the `.exe' into it. Now
     75   copy the MinGW libraries over to the same folder. Unfortunately,
     76   different Linux distributions have different locations (and sometimes
     77   even different names) for the MinGW executables. On Fedora, they’re
     78   found at `/usr/x86_64-w64-mingw32/sys-root/mingw/*'.
     79 
     80 
     81 Test on Windows
     82 ───────────────
     83 
     84   If you copy the whole `windows' folder to a Windows computer or VM,
     85   the program should now run as expected. Magically enough, [WINE] is
     86   also able to run the program from Linux.
     87 
     88   However, the program looks bad because
     89   • it uses GNOME’s Adwaita theme (which looks bad on Windows)
     90   • it uses GTK to render the client-side decorations (the top of the
     91     window with the exit button)
     92   • there’s no icon for the program in Windows’ taskbar
     93   • the program runs with a black command prompt window in the
     94     background.
     95 
     96   Furthermore, it’s not ideal to distribute the program by zipping up
     97   this folder and sharing it.
     98 
     99 
    100 [WINE] <https://en.wikipedia.org/wiki/Wine_(software)>
    101 
    102 
    103 Fix Icon
    104 ────────
    105 
    106   Download or create an icon.  From within the code you can set the icon
    107   for the program like this:
    108 
    109   ┌────
    110   │ GdkPixbuf *icon;
    111   │ icon = create_pixbuf("icon.ico");
    112   │ gtk_window_set_icon(GTK_WINDOW(window), icon);
    113   └────
    114   The code for `create_pixbuf' is:
    115   ┌────
    116   │ GdkPixbuf *create_pixbuf(const gchar * filename) {
    117    118   │    GdkPixbuf *pixbuf;
    119   │    GError *error = NULL;
    120   │    pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    121    122   │    if (!pixbuf) {
    123    124   │       fprintf(stderr, "%s\n", error->message);
    125   │       g_error_free(error);
    126   │    }
    127    128   │    return pixbuf;
    129   │ }
    130   └────
    131 
    132   This will change the icon of the program while it’s running. It shows
    133   up correctly in the Windows taskbar and window decorations, but it
    134   won’t change the icon of the actual `.exe'. I still recommend
    135   including this code because it works to set the icon on Linux desktop
    136   environments.
    137 
    138   To change the icon of the executable file on Windows, we have to use
    139   MinGW’s windres utility. Create a file called “icon.rc” with the
    140   contents:
    141 
    142   ┌────
    143   │ id ICON "icon.ico"
    144   └────
    145 
    146   and then run:
    147 
    148   ┌────
    149   │ x86_64-w64-mingw32-windres icon.rc -O coff -o icon.res
    150   └────
    151 
    152   Now we can add the `icon.res' resource file to the executable by
    153   slightly modifying our GCC command:
    154 
    155   ┌────
    156   │ $ x86_64-w64-mingw32-gcc -o main main.c icon.res `mingw64-pkg-config --cflags gtk+-3.0 --libs gtk+-3.0`
    157   └────
    158 
    159   Now the actual `.exe' file will have an icon associated with it on
    160   Windows.
    161 
    162 
    163 Remove Command Prompt window
    164 ────────────────────────────
    165 
    166   Just add the `-mwindows' flag to your compile command.
    167 
    168 
    169 Fix Window Decorations
    170 ──────────────────────
    171 
    172   You can disable the GTK client-side decorations by adding the
    173   following line to the code:
    174 
    175   ┌────
    176   │ putenv("GTK_CSD=0");
    177   └────
    178 
    179   Now the program should use the normal Windows exit button and drag
    180   bar.
    181 
    182 
    183 Switch to a Windows GTK theme
    184 ─────────────────────────────
    185 
    186   Adwaita looks bad on Windows. Search [Gnome-Look] for a different GTK3
    187   theme to use with your app. You can choose a flatter, lighter theme or
    188   one specifically made to look like Windows. For this example, I chose
    189   the “Windows10” theme.
    190 
    191   Download and extract the theme, then copy it to the `share/themes/'
    192   directory in the `windows/' directory we’ve been using.
    193 
    194   Then create a file called `settings.ini' in a `etc/gtk-3.0/' folder
    195   (also in the `windows/' directory) with the following contents:
    196 
    197   ┌────
    198   │ [Settings]
    199   │ gtk-theme-name=Windows10
    200   └────
    201 
    202   Now the program should look like native Windows software:
    203 
    204   <file:media/main_gtk_tutorial_screenshot.png>
    205 
    206 
    207 [Gnome-Look] <https://www.gnome-look.org/>
    208 
    209 
    210 Generate an installer
    211 ─────────────────────
    212 
    213   Here’s how to make an installer using a script called [nsiswrapper],
    214   so your users don’t have to manually unzip this `windows/'
    215   folder. Nsiswrapper generates an [NSIS] script and optionally compiles
    216   it using `makensis'.
    217 
    218   `cd' into the `windows/' directory, and run:
    219 
    220   ┌────
    221   │ nsiswrapper --run main.exe ./*
    222   └────
    223 
    224   This should generate a new file called `installer.exe', which you can
    225   use to distribute the program. The installer is a standard GUI wizard
    226   that Windows users will be accustomed to. It allows users to
    227   optionally add a desktop shortcut, start menu entry, and taskbar
    228   shortcut.
    229 
    230   Notably, it also includes an uninstaller.
    231 
    232 
    233 [nsiswrapper] <https://src.fedoraproject.org/rpms/mingw-nsiswrapper>
    234 
    235 [NSIS] <https://sourceforge.net/projects/nsis/>
    236 
    237 
    238 Automating builds
    239 ═════════════════
    240 
    241   You may have noticed that I’ve been using Fedora to compile this
    242   program. I’m actually running Arch Linux, but MinGW packages aren’t
    243   easily available for Arch, so I’ve been using a [Linux container] to
    244   take advantage of Fedora’s package system.
    245 
    246   I’m using [Buildah] instead of Docker. I wrote a script to automate
    247   the following tasks
    248 
    249   • set up a named fedora container
    250   • install the necessary packages using dnf
    251   • save the container so I don’t have to download dependencies again
    252   • compile the GTK program using the steps I’ve outlined so far
    253   • generate the installer
    254 
    255   Before you look at the script, I recommend reading the [buildah
    256   introduction tutorial].
    257 
    258   ┌────
    259   │ #!/bin/bash
    260   │ setup () {
    261   │     # Create a fedora container
    262   │     container=$(buildah from fedora)
    263    264   │     # Install dependencies
    265   │     buildah run $container dnf -y install mingw64-gtk3 mingw32-binutils mingw32-nsiswrapper
    266    267   │      # Fix typo in mingw library
    268   │     buildah run $container bash -c "sed -i -e 's/-Wl,-luuid/-luuid/g' /usr/x86_64-w64-mingw32/sys-root/mingw/lib/pkgconfig/gdk-3.0.pc"
    269    270   │     # Cache image to avoid re-downloading dependencies every time
    271   │     buildah commit $container my-gtk-app
    272    273   │     # Clean up
    274   │     buildah rm $container
    275   │ }
    276    277   │ build () {
    278   │     # Create a new container from the base one we created
    279   │     container=$(buildah from localhost/my-gtk-app)
    280    281   │     # Folder to hold everything needed for the windows installer:
    282   │     output_folder=windows
    283   │     yes | rm -r $output_folder
    284   │     mkdir $output_folder
    285    286   │     # Directory of your package in the container
    287   │     folder=/root/app
    288    289   │     # Copy program into container
    290   │     buildah copy $container . $folder
    291    292   │     # Name for the executable file on Windows
    293   │     executable_name="Demo.exe"
    294    295   │     # make some folders we'll need
    296   │     buildah run $container bash -c "cd $folder && mkdir -p $output_folder/share/themes $output_folder/etc/gtk-3.0"
    297    298   │     # generate a RC file with the windres utility to embed an icon into the .exe later on
    299   │     buildah run $container bash -c "cd $folder && x86_64-w64-mingw32-windres icon.rc -O coff -o icon.res"
    300    301   │     # copy some project resources
    302   │     buildah run $container bash -c "cd $folder && cp -r Windows10 $output_folder/share/themes && \
    303   │     cp settings.ini $output_folder/etc/gtk-3.0/ &&\
    304   │     cp icon.ico $output_folder"
    305    306   │     # Compile program
    307   │     buildah run $container bash -c "cd $folder && x86_64-w64-mingw32-gcc -mwindows -o $executable_name main.c icon.res \`mingw64-pkg-config --cflags gtk+-3.0 --libs gtk+-3.0\`"
    308    309   │     # Copy executable into installation folder
    310   │     buildah run $container bash -c "cd $folder && cp $executable_name $output_folder"
    311    312   │     # Copy mingw dlls into installation folder
    313   │     # This part may need to be personalized
    314   │     buildah run $container bash -c "yes | cp -r /usr/x86_64-w64-mingw32/sys-root/mingw/{bin/*.dll,share} $folder/$output_folder/"
    315    316   │     # Generate an installer
    317   │     buildah run $container bash -ic "cd $folder/$output_folder && nsiswrapper --run $executable_name ./*"
    318    319   │     # Copy the output from the container
    320   │     cp -ru $(buildah unshare buildah mount $container)$folder/$output_folder .
    321    322   │     # Clean up
    323   │     buildah rm $container
    324   │ }
    325    326   │ # This just checks whether the container already exists on your drive
    327   │ buildah inspect localhost/my-gtk-app &>/dev/null
    328   │ return_value=$?
    329    330   │ if [ $return_value -eq 1 ]
    331   │ then
    332   │     echo "Initial container setup"
    333   │     setup
    334   │ fi
    335    336   │ # Build project
    337   │ build
    338   └────
    339 
    340 
    341 [Linux container] <https://en.wikipedia.org/wiki/LXC>
    342 
    343 [Buildah] <https://github.com/containers/buildah>
    344 
    345 [buildah introduction tutorial]
    346 <https://github.com/containers/buildah/blob/master/docs/tutorials/01-intro.md>
    347 
    348 
    349 References
    350 ══════════
    351 
    352   • [windows - How do I add an icon to a mingw-gcc compiled executable?
    353     - Stack Ov…]
    354   • [Disable client side decorations (GTK_CSD) by default on Windows
    355     (win32) (#760…]
    356   • [Compile for Windows on Linux | BlogCompiler]
    357 
    358 
    359 [windows - How do I add an icon to a mingw-gcc compiled executable? -
    360 Stack Ov…]
    361 <https://stackoverflow.com/questions/708238/how-do-i-add-an-icon-to-a-mingw-gcc-compiled-executable>
    362 
    363 [Disable client side decorations (GTK_CSD) by default on Windows (win32)
    364 (#760…] <https://gitlab.gnome.org/GNOME/gtk/issues/760>
    365 
    366 [Compile for Windows on Linux | BlogCompiler]
    367 <https://www.blogcompiler.com/2010/07/11/compile-for-windows-on-linux/>