Navigation Content Footer

Building a Windows Installer

07/19/2011 — After investing a couple of evenings, Gibbon now builds and runs under Windows. It now even has a setup.exe that automatically installs or uninstalls the software. While the story of that installer will be completely boring for the vast majority of people, programmers - especially with a Unix background - may be interested in hearing about it.

Gibbon can be built under Windows with the MinGW (http://www.mingw.org) development environment. You will find tons of information about how to compile and run Gtk based programs under Windows but I only found one good article (http://blogs.gnome.org/nacho/2009/06/02/porting-your-gtk-application-to-windows/) about packing things together into a self-extracting executable installer image. That article was of big help but the information is a little outdated.

Migrate to GSettings!

One of the biggest obstacles was GConf as it turned out. Starting Gibbon from the command line worked, but as soon as the program was installed outside of the MinGW environment, it tried to execute gconfd-2.exe and that failed.
Adjusting the PATH (see below) did not help, and in the end I decided to port all GConf stuff to GSettings. That went relatively quick, and it actually saved me some lines of code. And, best of it all, once using GSettings instead of GConf, Gibbon ran like a charm.

Anatomy of a Windows Installation Pack

Under Windows you cannot assume that all dependencies for a Gtk application (glib, cairo, atk, ...) are installed system-wide. Besides, versioning of shared libraries under Windows is merely a joke, and therefore the best approach is to ship and install all dependencies with your application. You could let your users decide to link against an already installed runtime environment at install time. I think that this is a recipe for trouble and decided against it.
Basically, your application later runs in a minimal unix like directory structure, with only one application installed. The prefix for the installationo (normally something like ”C:\Program Files (x86)“ or so) can be selected by the installing user. Underneath you see a directory structure like under /usr or /usr/local on your Unix machine.
One notable difference is the directory $PREFIX/bin. It holds the executable file itself (in my case gibbon.exe) plus all “shared” libraries (all ending in dll under Windows). Stuffing them into the same directory ensures that they can be loaded at run-time.

Relocatable Paths

As a consequency of this structure, hard-coded paths like /usr/share or /etc/gtk-2.0 do not work under Windows. Well, they do work, but only in the MinGW environment that follows this structure.
I therefore had to add a couple of #ifdef MSDOS preprocessor directives in order to adjust several paths used by Gibbon. Candidates for such directories are the locale directories for your mo files, and directories for extra data, typically installed into /usr/share.
Glib helps you with the function g_win32_get_package_installation_directory_of_module() that spits out the directory from where your program was run (without the trailing /bin).
Additionally make sure to assemble paths with g_build_filename() and friends, and to not hard-code directories with Unix semantics. Use g_get_user_data_dir(), g_get_user_config_dir() or similar functions instead!

Nullsoft Scriptable Install System (NSIS)

In order to build that installer package you need specialized software. I took two of them into consideration, Nullsoft Scriptable Install System (NSIS) and Inno Setup.
I first went with NSIS (http://www.nsis.org/), mostly because I first tried to copy the install stuff from Pidgin (http://www.pidgin.im/). NSIS is quite powerful, and reminds a little bit of a scripting language that has grown uncontrolled over time.
I first tried to just copy the pidgin source script for NSIS, and then delete unneeded stuff, and do a s/Pidgin/Gibbon/g replacement. That was by far not enough and I made up my mind that the way pidgin does things is overkill for my purposes.
Pidgin basically downloads a lot of ZIP files with pre-compiled binaries for Windows into the installation pack, and then the installer unpacks them at install time and installs them to the desired location.

Inno Setup

I then searched for other Gtk ports to Windows and came across gedit (http://www.gedit.org/), the Gnome (http://www.gnome.org/) standard editor. That looked a lot simpler to me.
They use Inno Setup (http://www.jrsoftware.org/isinfo.php) instead of NSIS. My guess is that NSIS is more powerful, especially for native Windows applications, but it has a steep learning curve. Inno Setup, on the other hand, is really simple, and perfectly suited for the Cut & Paste approach that I had in mind.

Collecting Your Files

By the way, everything described below is included in the Gibbon sources. If you are interested in the details, go to the Download section, and get them from there.
So, how do you put together all necessary files?
I created a subdirectory “msdos” in Gibbon, and used a script “build-installer.sh” from gedit for packing things together.
The script first creates a subdirectory “installer”, and in there two additional subdirectories “gtk” and “gibbon”. I did not understand why I need two distinct directories but it was like this for gedit, and I simply copied things from there.
Inside these subdirectories, a mini Unix directory layout is created with “bin”, “etc”, “lib”, “share”, ... and the files from your current installation are simply copied there. I like this approach a lot better than the pidgin way of doing things and downloading installation tar balls from the internet. The files used for the Gibbon installer are exactly those that I run the application with locally.
Important: The files are copied from where they are installed! They do not come from the build directory. This is safer because libtool will relink a lot of stuff when installing. But you have to make sure that you have always run “make install” in the top-level directory before you create an installer.
I also made “build_installer.sh” source a file “build_installer.sh.include” if it is found. The include file is not in Git or the distribution. It is included after some variables like the global installation prefix or the path to the Inno Setup compiler are set, and you can override some of the values there.
Then, finally the script invokes the Inno Setup compiler ISCC, and creates the installer executable from what the script has copied together.

Querying Modules

The installer would work at this point but the application would not run correctly because some stuff is missing.
Gtk loads input modules at runtime. On a Unix system, the list of modules is read from “/etc/gtk-2.0/gtk.immodules”. This file contains absolute paths to the installed input modules.
In order for the application to be relocatable this file has to be generated during the installation process on the end user's machine. For that purpose, a MS-DOS batch file “querymodules.bat” is generated from the build script, and that batch file is executed in a post-installation hook. You do that by adding a corresponding entry to the [Run] section of your Inno Setup source file.
Still, you should create empty files with the same name in the installation directory so that Inno Setup knows about their presence. Otherwise, when uninstalling the software, the files do not get removed, and your applications leaves some scattered useless files behind.

Trouble With librsvg

Gibbon uses SVG icons. In order to make that work, GdkPixbuf has to be able to grok with that file format.
I had to make sure that librsvg plus the pixbuf loaders get included in the installation directory. And like for the input modules (see above), I added another line to the generated MS-DOS batch file that creates the pixbuf loader cache file “$PREFIX/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache”.

Compiling GSettings Schemas

The input files for GSettings are XML but they get compiled into a binary format. This can be done while building the installer. You just have to call “glib-compile-schemas” with the directory of your schema files as the argument.
This directory is, of course, the one that you created in the directory with the installation files.

Multiple Languages

Inno Setup can run the installation process in multiple languages. Gibbon supports English, German, and Bulgarian, and I added this section to the Inno Setup source file “gibbon.iss.in” (which is the autoconf source for the real source file „gibbon.iss“):

[Languages]
Name: "en"; MessagesFile: "compiler:Default.isl"
Name: "de"; MessagesFile: "compiler:Languages\German.isl"
Name: "bg"; MessagesFile: ".\Bulgarian-5.1.11.isl"


Inno Setup ships with English and German language files. I had to download the Bulgarian one from this place: http://www.jrsoftware.org/files/istrans/
Basically, they are just plain text files. In doubt, you can convert them to a format that gettext understands and maintain the translations with the normal PO file stuff. I was too lazy for that.

Uninstall Regkey

Gibbon does not use the Windows registry directly. But it uses GSettings, and GSettings now has a backend that stores configuration stuff in the Windows Registry.
In the input file for Inno Setup, I added these lines:

[Registry]
Root: HKCU; Subkey: "Software\GSettings\apps\gibbon"; Flags: uninsdeletekey
Root: HKCU; Subkey: "Softare\GSettings\apps"; Flags: uninsdeletekeyifempty
Root: HKCU; Subkey: "Softare\GSettings"; Flags: uninsdeletekeyifempty


That unconditionally removes all regkeys that Gibbon has created.
It then also tries to remove all GSettings stuff if it is empty. This will be the case, when Gibbon was the only application that used GSettings for that particular user.

No EULA Nonsense!

A long time ago, Microsoft started bugging users with clickable bogus End-User License Agreements (EULA). They try to fool people into thinking that clicking some ”I agree to this ...” could constitute a valid contract between the software distributor and the user installing the software.
Legally spoken, this is bare nonsense! And it is against all Free Software spirit to boot.
Unfortunately, even some Free Software follows this ridiculous practice. Many Windows installers display the GPL (or other licenses) and force the user to “agree“ to the GPL by clicking two buttons. In the Inno Setup .iss file you will then find something like:

[Setup]
...
LicenseFile=whatever\share\doc\COPYING
...


Bugging users with a rather longish legal blurb in English is indecent for an internationalized Free Software. I created a file license.txt in the installation directory. That file just contains the three regular paragraphs with the GPL summary, the ones that you know from most C source files.
The contents of this file is displayed by the installer (without forcing any “agreement“ clicks):

[Setup]
...
InfoBeforeFile=.\license.txt
...


I think that this is a good compromise. People are made aware of Free Software licenses but they are not tyrannized by the software.

Application Icon and File Information

You may have noticed that icons for programs under Windows often use custom icons. These icons and other resources are linked into the executable image as special symbols. Fortunately, MinGW comes with a tool called windres.exe that allows you to create such modules.
First you should create a 48x48 icon for your image and save it in Windows icon format (.ico). Gimp (http://www.gimp.org/) can create that format.
Then you create an input file for windres.exe. This is “gibbon.rc“:

A ICON "../share/icons/application/gibbon.ico"
1 VERSIONINFO
FILEVERSION 0,0,1,0
PRODUCTVERSION 0,0,1,0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "Guido Flohr"
VALUE "FileDescription", "A free client for FIBS"
VALUE "FileVersion", "0.1.1.0"
VALUE "InternalName", "gibbon"
VALUE "LegalCopyright", "Guido Flohr"
VALUE "OriginalFilename", "gibbon.exe"
VALUE "ProductName", "Gibbon"
VALUE "ProductVersion", "0.1.1.0"
END
END

BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END


The first line defines the icon to link into the executable image.
The rest adds other information to the file. You can see that by right-clicking on the file “gibbon.exe”. Before you have installed Gibbon, you must right-click “src/.libs/gibbon.exe”, not “src/gibbon.exe”!
Just copy the file and do not try to understand it. Just edit the few bits and pieces that have to be modified like version and name.
I found this information at http://stackoverflow.com/questions/708238/how-do-i-add-an-icon-to-a-mingw-gcc-compiled-executable which also points to http://msdn.microsoft.com/en-us/library/aa381058.aspx
You have to compile this rc file into a regular object file. The corresponding snippet from Gibbon's „src/Makefile.am” looks like this:

if MSDOS
gibbon-res.o: gibbon.rc ../share/icons/application/gibbon.ico
$(WINDRES) -i gibbon.rc --input-format=rc -o $@ -O coff

gibbon.rc: gibbon.rc.in

platform_libadd += gibbon-res.o -ldnsapi -lws2_32
endif


You might think that the resource information should be localized to all languages that your application supports. I think that this is not possible. Windows seems to expect that a program image on disk only supports one single language. I therefore picked American English aka the POSIX or C locale.

Uninstall Previous Versions During Upgrade

Inno Setup does not automatically uninstall previously installed versions. That means that files would not be removed from the installation target, when they are removed from the release. Therefore, any previous version should be uninstalled prior to upgrading. Fortunately, Inno Setup stores the path to the uninstaller in the Windows registry:

function GetUninstallCommand(): String;
var
UninstallExe: String;
Command: String;
begin
UninstallExe := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\Gibbon_is1');
Command := '';
// Prefix can be HKLM or HKCU.
if not RegQueryStringValue(HKLM, UninstallExe, 'UninstallString', Command) then
RegQueryStringValue(HKCU, UninstallExe, 'UninstallString', Command);
Result := Command;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
Command: String;
var ExitCode: Integer;
begin
if (CurStep = ssInstall) then
begin
Command := GetUninstallCommand ();
if (Command <> '') then
begin
Command := RemoveQuotes (Command);
// If that fails, a warning or error message will most
// probably pop up. We therefore ignore the return
// value.
Exec (Command, '/SILENT /NORESTART /SUPPRESSMSGBOXES',
'', SW_HIDE, ewWaitUntilTerminated, ExitCode);
end;
end;
end;


This is mostly a variation of an idea by Craig McQueen.

I know that this is not really a step-by-step guide-through tutorial. For details, check out the Gibbon sources, and grep. I think with the explanations given above you will be able to identify and understand all relevant parts.
Thanks again to Nacho from the gedit team for the excellent information he gave at http://blogs.gnome.org/nacho/2009/06/02/porting-your-gtk-application-to-windows/

blog comments powered by Disqus