Packaging

Intro

While ooc provides facilities to write highly cross-platform applications, and makes it rather easy to develop on all these platforms, distributing standalone applications is another matter entirely.

Until a tool comes along and in the darkness binds them, this chapter documents the manual process that goes into releasing neatly packaged software for Windows, OSX and Linux.

Windows

Windows is surprisingly easy to package for — a 32-bit executable will happily run on both 32-bit and 64-bit systems, there is no library path headache if you include all the required DLLs in the same directory as the .exe, and the current working directory is always the one with the .exe file.

Dynamic GC

If your application is multi-threaded, make sure to link with the GC dynamically as explained in the Garbage Collection chapter.

If you link statically with the GC and use multiple threads, chances are you will run into very ugly memory corruption bugs that seemingly make no sense. So, make sure to double-check that first.

Since you’ll link dynamically with it, you’ll need to include libgc-1.dll, and you might also need pthreadGC2.dll, which lives in /mingw/bin/ under MinGW.

Resource file

To bundle an icon with your executable, and add the full program name, author, etc, create a .rc file containing something like that:

id ICON "art/foo.ico"
1 VERSIONINFO
FILEVERSION     1,0,0,0
PRODUCTVERSION  1,0,0,0
BEGIN
  BLOCK "StringFileInfo"
  BEGIN
    BLOCK "040904E4"
    BEGIN
      VALUE "CompanyName", "Company Name"
      VALUE "FileDescription", "Application Name"
      VALUE "FileVersion", "1.0"
      VALUE "InternalName", "applicationname"
      VALUE "LegalCopyright", "Author Name"
      VALUE "OriginalFilename", "foo.exe"
      VALUE "ProductName", "Application Name"
      VALUE "ProductVersion", "1.0"
    END
  END

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

Adjust these values as needed. To create a high-quality .ico file from a PNG image, the usage of a tool such as IcoFX might be needed.

Then, compile this .rc file with the windres command-line utility - if you have a Makefile you might want to add a target for this.

windres -i foo.rc -o foo.res -O coff

The final step to include these resources is to link them with your executable. This can be done easily if you already have a .use file, by adding a section in a version(windows) block. For example, in foo.use:

Name: foo
Version: 1.0
Description: Foo builds upon bar baz and does wonderful stuff
SourcePath: source
Requires: bar, baz
Main: foo/foo

# Add resources on Windows
version (windows) {
  Libs: ./foo.res
}

Make sure to use foo in the main file of your application so that rock takes these into account. The resulting executable should have the icon built-in, along with the author and product information you have specified in the foo.rc file.

Note that every time foo.rc changes, it needs to be recompiled to foo.res. You don’t need to distribute either the .rc or the .res file with the executable, it’s all baked in!

DLL dependencies

To figure out which libraries someone will need to run your application, you can use Dependency Walker, also known as depends.exe.

Loading your .exe in it, with the right working directory, will allow you to see which DLLs it loads and from where. Then, all you need is to copy those DLLs to the directory of your application and distribute them along.

Be warned: there may be several versions of a given library on your system. Make sure to pick the right one.

If your program can be launched by double-clicking on it in the Windows GUI (instead of running it from the command-line), it’s a good sign - but it might not be enough. Some open-source programs install libraries to system paths, and your application may be relying on that.

To make sure the application runs everywhere, testing it on a “virgin” install of Windows is recommended. If there was a time to use a virtual machine, that would be it.

Final notes

You can choose to generate an installer of course, but a .zip file is fine as well. Windows systems usually have facilities to extract .zip files without the need for an external program.

Creating a zip file can be done with the command line GNU zip utility:

zip -r Foo-1.0-Windows.zip Foo-1.0-Windows

..or by using a graphical tool, for example, 7-Zip

OSX

Releasing an app on OSX is relatively easy, as tools exist to make it easier. 64-bit executables are the norm for recent versions of Mac OS X, so there is no pain there either.

The only part where it needs a little hand holding is when creating the app bundle.

dylibbundler

We need to distribute libraries inside the app bundle - however, so that the paths are resolve correctly, we’ll need to use dylibbundler to modify the executable and ‘fix’ the paths to these libraries.

With Homebrew, installing dylibbundler is as simple as doing:

brew install dylibbundler

The first step is to tell the C compiler to reserve enough room to modify the library paths later:

rock +-headerpad_max_install_names

Then, we’ll have to create a folder that will be our app bundle, such as Foo.app, with the following directory structure:

Foo.app/
  Contents/
    Info.pList
    MacOS/
      foo
      wrapper
    Resources/
      foo.icns
    libs/

Contents/MacOS/foo is our executable that we have copied from before, and Contents/MacOS/wrapper will be our launcher script (described in a further section).

The Contents/MacOS directory should also contain any files the executable expects to find in the current working directory when launched (e.g. for game that would be the graphics, sounds, etc.)

The next step is to dylibbundler on the executable so that libs are copied and the paths are fixed:

dylibbundler -od -b -x ./Foo.app/Contents/MacOS/helloworld -d ./Foo.app/Contents/libs/

Info.pList

The Info.pList file is similar to the foo.rc file we discussed in the Windows section. A stock plist file looks something like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleGetInfoString</key>
  <string>Application Name</string>
  <key>CFBundleExecutable</key>
  <string>wrapper</string>
  <key>CFBundleIdentifier</key>
  <string>com.yourdomain.www</string>
  <key>CFBundleName</key>
  <string>applicationname</string>
  <key>CFBundleIconFile</key>
  <string>foo.icns</string>
  <!-- Don't modify those! -->
  <key>CFBundleShortVersionString</key>
  <string>1.0</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>IFMajorVersion</key>
  <integer>0</integer>
  <key>IFMinorVersion</key>
  <integer>1</integer>
</dict>
</plist>

It should live in Foo.app/Contents/Info.pList. Just like a Windows RC file, it references a foo.icns file. A tool like img2icns can be used to convert a high-resolution PNG image to a Mac OSX icon.

Launcher script

Finally, we have to use a launcher script so that the current working directory of the app will be correct.

#!/bin/bash
cd "${0%/*}"
./foo

Troubleshooting

It’s not uncommon to encounter an error if an app Bundle is malformed or otherwise problematic. Instead of double-clicking on the .app bundle to launch it, one can open it from the command line:

open Foo.app

If you get an “error -10180”, it means something went wrong while launching the app. Make sure the files inside Contents/MacOS/ are executable, and that the wrapper is trying to launch the correct executable.

In doubt, running plutil can help proofread the pList file:

plutil Foo.app/Contents/Info.pList

Final notes

OSX apps are sometimes encountered as .dmg files in the wild, which are image files that contain partitions, which can be mounted and read from. This allows a nice “drag and drop” window to be displayed.

However, distributing an OSX application by just zipping up the .app bundle is acceptable. It can be done either on the command line:

zip -r Foo.OSX.zip Foo.app

…or in the graphical interface (Right Click -> Compress Foo.app).

Linux

Linux may be one of the comfiest platform for ooc development, but ironically, it is one of the most painful to package standalone applications for. As a rule of thumb, do not assume that your users will want to install libraries themselves — always package them with the software.

Multiarch

64-bit installs of Linux that aren’t multi-arch will complaing about missing 32-bit libraries, and conversely, 32-bit installs won’t be able to run a 64-bit application at all. Which leaves us with the only option of providing both a 32-bit and a 64-bit executable.

An example folder hierarchy is:

foo-1.0-linux/
  bin/
    foo32
    foo64
    libs32/
    libs64/
  foo.sh

Since we are going to ship dynamic libraries with the app, we should specify the path where they are with the -rpath linker option. The rock command looks a little bit like this:

rock +-Wl,-rpath=bin/libs32 -o=foo32

…for the 32-bit version, and similarly for the 64-bit version.

Chroot

One of the easiest ways to build both a 32-bit and a 64-bit executable is to have a 64-bit VM of Ubuntu, and set up a 32-bit chroot inside of it, then copy files out of the chroot to retrieve the binaries and associated libraries.

Setting up a chroot with debootstrap is documented on the Ubuntu website.

Copying libs

To figure out the libraries you need to copy to libs32 or libs64, the ldd command line utility can be used. Filtering its output to remove a few libraries always present on Linux systems can help:

ldd foo64 | egrep "(/usr/lib/)|(prefix64)" | cut -d ' ' -f 3 | egrep -v "lib(X|x|GL|gl|drm)"

The example above works for an OpenGL-based application. Copying these files automatically can be done with a shell script such as:

for l in $(ldd foo64 | egrep "(/usr/lib/)|(prefix64)" | cut -d ' ' -f 3 | egrep -v "lib(X|x|GL|gl|drm)" | tr '\n' ' '); do
  cp $l libs64/
done

Checking that it well can be done by running ldd bin/foo64 in the release directory. Here’s some example output that shows the libs are resolved to their relative paths:

$ ldd bin/foo64
libSDL2-2.0.so.0 => bin/libs64/libSDL2-2.0.so.0 (0x00007fc6fa615000)
libSDL2_mixer-2.0.so.0 => bin/libs64/libSDL2_mixer-2.0.so.0 (0x00007fc6f9ee0000)
libmxml.so.1 => bin/libs64/libmxml.so.1 (0x00007fc6f9cd3000)
libfreetype.so.6 => bin/libs64/libfreetype.so.6 (0x00007fc6f9a37000)
libyaml-0.so.2 => bin/libs64/libyaml-0.so.2 (0x00007fc6f9815000)

Launcher script

Courtesy of , such a launcher script will detect the architecture of the machine it’s running on and launch the right executable:

#!/bin/bash
# Move to the script's directory
cd "`dirname "$0"`"

# Get the kernel/architecture information
UNAME=`uname`
ARCH=`uname -m`

# Pick the proper executable
if [ "$ARCH" == "x86_64" ]; then
  ./bin/foo64
else
  ./bin/foo32
fi

Final notes

The .tar.gz or .tar.bz2 archive formats is well-suited to distribute applications for Linux, but .zip works just as well, and .tar.xz is usually a little smaller.

To create a .tar.gz, do:

tar czvf foo-1.0-linux.tar.gz foo-1.0-linux

To create a .tar.bz2, use cjvf instead.

To create a .tar.xz:

tar cf --xz foo-1.0-linux.tar.xz foo-1.0-linux

To create a .zip, do:

zip -r foo-1.0-linux.tar.zip foo-1.0-linux