joliclic code

[version française]

Distribute your XULRunner app

5 - Synthesis - all platforms

5.1 Changes to the app

Let summarize the changes we have made to the application itself, for each platform:

So, finally these are small changes, and all of them can be added globally, because they don't disturb the untargeted platforms.

5.2 Added data

For a better integration to each platforms, we have added some specific data:

5.3 Created installers

5.4 A global and reusable script

In each chapters, we have used some dedicated bash scripts to create all our installers and other data.
I propose now a global script to performs all the tasks in one shot.

More, this script can be used with any other XULRunner application, we only have to adapt a config file.

I don't write this script here, you'll find it in the joined archive of this chapter, but here's the config file:

#!/bin/bash

#_______________________________________________________________________________
#
# script version: 1.1
# date: 2011-06-15
#_______________________________________________________________________________

# exit the script on errors
set -e

#_______________________________________________________________________________
#
# common configuration
#_______________________________________________________________________________

# The name of the application, will be the name of the executable for example.
# It should contain only [a-zA-Z_-.] and no whitespace.
APP_NAME=myapp

# The name of the application displayed on screen.
APP_DISPLAY_NAME=MyApp

# The absolute path to the directory containing this script. Don't modify this
# variable if you don't know what you are doing.
CUR_DIR=$(dirname "$0")
CUR_DIR=$(readlink -f -- "$CUR_DIR")

# The main folder of the sources (absolute path), containing the app, data,...
# Can be defined before, in some other scripts.
if [ ! $MAIN_DIR ]; then
    MAIN_DIR=$(readlink -f -- "$CUR_DIR/..")
fi

# The folder where all resulted builds (installers, archives,...) will be copied
DIST_DIR=$MAIN_DIR/dist

# The temporary folder where all build are done
TMP_DIR=$MAIN_DIR/tmp

# Folder which contains extra useful programs for the build script
TOOLS_DIR=$MAIN_DIR/tools

# Folder which contains all build scripts, and the current config file
BUILDERS_DIR=$MAIN_DIR/builders

# The folder of the real sources of the XULRunner application
APP_SRC_DIR=$MAIN_DIR/$APP_NAME

# The folder of the extra data of the app (icons,...)
APP_DATA_DIR=$MAIN_DIR/data

# The folder of the used icons
# This folder must contains:
# icon16.png, icon22.png, icon26.png, icon32.png, icon40.png, icon48.png,
# icon128.png, icon48.svg, icon.ico, icon.icns, icon48.txt
# You can find some shell scripts in the data/icons folder to create the ico,
# icns et base64 icons.
APP_ICON_DIR=$APP_DATA_DIR/icons

# The current version of the app
APP_VERSION=1.0

# You can use a file named version in the main directory too, and uncomment the
# following line to set this variable
#APP_VERSION=$(cat "$MAIN_DIR/version")

# if the app contains some compiled code, and so it's architecture dependent,
# uncomment the following line
#ARCH_DEPENDENT=1


#_______________________________________________________________________________
#
# Linux specific
#_______________________________________________________________________________

# the folder which contains the Linux specifics data
LINUX_DIR=$APP_DATA_DIR/linux

# the desktop file, will be renamed as $APP_NAME.desktop
DESKTOP_FILE=$LINUX_DIR/desktop

# If this variable is set, the desktop file will be adapted, i.e. the strings
# @@APP_VERSION@@, @@APP_NAME@@, @@APP_DISPLAY_NAME@@, and
# @@APP_DESKTOP_CATEGORIE@@,..., will be replaced by their values. Simply
# comment the following line to not use this feature.
OPT_ADAPT_DESKTOP=1

# this value is displayed in a tooltip by the desktop
APP_DESKTOP_COMMENT="a Hello World XULRunner app"

# A value chosen in http://standards.freedesktop.org/menu-spec/latest/apa.html
# If you use multiple value, use a semicolon (;) as separator
# DON'T forget the last semicolon, even if there is only one value
APP_DESKTOP_CATEGORIE='Utility;'

# You can use this variable to put extra lines to the Desktop file, for example
# a localized comment
APP_DESKTOP_EXTRA='Comment[fr]=un Bonjour Monde pour XULRunner.\n'

# the generic Linux launcher, a shell script, for our app. Will be renamed as
# $APP_NAME.sh
LINUX_LAUNCHER=$LINUX_DIR/launcher.sh


#_______________________________________________________________________________
#
# deb package specific
#_______________________________________________________________________________

# Comment the following line if you don't want to generate a deb package
OPT_BUILD_DEB=1

# the debian folder used to build our generic deb
DEBIAN_DIR=$APP_DATA_DIR/debian

# If this variable is set, the files 'changelog', 'control', 'menu',
# 'myapp.link', and 'copyright' of the debian folder will
# be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,
# @@APP_DISPLAY_NAME@@, @@DEB_CONTROL_VERSION@@, @@DEB_CONTROL_SECTION@@,
# @@DEB_MENU_SECTION@@, ..., will be replaced by their values. Simply comment
# the following line to not use this feature.
OPT_ADAPT_DEBIAN=1

# The version of the generated deb package.
DEB_CONTROL_VERSION=${APP_VERSION}-1

# the category of the app. See http://packages.debian.org/en/sid/
DEB_CONTROL_SECTION=Utilities

DEB_CONTROL_MAINTAINER='John Doe <johndoe@example.com>'

# author of the generated deb
DEB_CONTROL_AUTHOR=$DEB_CONTROL_MAINTAINER

DEB_CONTROL_HOMEPAGE='<http://example.com/myapp/>'

# name of the generated package
DEB_CONTROL_PACKAGE=$APP_NAME

# long description of the app for the generated deb. Can be multiline, see
# http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description
# for the formatting (in short, add at least a space at the beginning of all
# lines apart the first)
DEB_CONTROL_DESCRIPTION='simple Hello World.
 Powered by XULRunner.'

# if the app have other dependencies than Firefox, list them, separated by a
# comma. Important, this list MUST begin by a comma.
DEB_CONTROL_EXTRA_DEPENDENCIES=

# if the app contains some compiled code, and you want to use the
# ${shlibs:Depends} variable with debhelper, uncomment the following line
#DEB_CONTROL_USE_SHLIBS_DEPENDS=1

# the category for the Debian menu, see
# http://www.debian.org/doc/packaging-manuals/menu-policy/ch2.html#s2.1
DEB_MENU_SECTION='Applications/Programming'

DEB_DATE="$(date -R)"

DEB_COPYRIGHT='<Copyright (C) 2010 John Doe>'

# the short license for the generated deb
DEB_SHORT_LICENSE=$(cat "$LINUX_DIR/deb_short_license.txt")


#_______________________________________________________________________________
#
# maemo deb specific
#_______________________________________________________________________________

# Note that the debian specific variables are used as well to generate the
# maemo deb, so they must set.

# the APP_NAME specific for Maemo, can be the same as APP_NAME
MOBILE_APP_NAME=$APP_NAME

# the APP_DISPLAY_NAME specific for Maemo, can be the same as APP_DISPLAY_NAME
MOBILE_APP_DISPLAY_NAME=$APP_DISPLAY_NAME

# the name of the app for the package. Should be different than APP_NAME,
# otherwise there can be collision with the generic deb created by the other
# script
MAEMO_PKG_APPNAME=${APP_NAME}-mobile

# the desktop file for Maemo, will be renamed as $APP_NAME.desktop
MAEMO_DESKTOP_FILE=$LINUX_DIR/maemodesktop

# the launcher for Maemo, a shell script, for our app. Will be renamed as
# $APP_NAME.sh
MAEMO_LAUNCHER=$LINUX_DIR/maemolauncher.sh

# Comment the following line if you don't want to generate a deb package
OPT_BUILD_MAEMO_DEB=1

# the debian folder used to build our deb for Maemo
MAEMODEBIAN_DIR=$APP_DATA_DIR/maemodebian

# the categorie of the app for Maemo. See
# http://wiki.maemo.org/Packaging/Guidelines#Sections
DEB_CONTROL_SECTION_MAEMO=user/utilities

# the name of the generated deb for Maemo.
# Should really the same as MAEMO_PKG_APPNAME, see its comment.
DEB_CONTROL_PACKAGE_MAEMO=$MAEMO_PKG_APPNAME

# the description of the generated deb, especially for Maemo. See the comments
# for DEB_CONTROL_DESCRIPTION in this file for formatting.
DEB_CONTROL_DESCRIPTION_MAEMO=$DEB_CONTROL_DESCRIPTION

# like DEB_CONTROL_EXTRA_DEPENDENCIES, but for Maemo
DEB_CONTROL_EXTRA_DEPENDENCIES_MAEMO=

# the base64 file (text) of the icon for the Maemo deb.
# Can be generated by the script 'build_icon_base64.sh' in the data/icons dir.
MAEMO_BASE64_ICON="$APP_ICON_DIR/icon48.txt"


#_______________________________________________________________________________
#
# rpm package specific
#_______________________________________________________________________________

# Comment the following line if you don't want to generate a rpm package
OPT_BUILD_RPM=1

# path to the spec file used to generate the rpm
RPM_SPEC_FILE=$LINUX_DIR/rpmspec

# If this variable is set, the .spec file will
# be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,
# @@APP_DISPLAY_NAME@@, @@RPM_VERSION@@, @@RPM_RELEASE@@, @@RPM_GROUP@@,
# @@RPM_LICENSE@@, @@RPM_URL@@, @@RPM_SOURCE@@, @@RPM_DATE@@, ...
# will be replaced by their values. Simply comment the following line to not use
# this feature.
OPT_ADAPT_RPM=1

# version of the app for the spec file: must contain only integer and digit, no
# strings like 'beta'
RPM_VERSION=$APP_VERSION

# the version of the generated rpm, not the app version
RPM_RELEASE='1%{?dist}'

# the short version of the generated rpm, without any string relative to the
# distribution or architecture
#RPM_RELEASE_SHORT=1
RPM_RELEASE_SHORT=$(echo $RPM_RELEASE | sed "s/%.*$//")

# breve summary of the app for the package. One line only.
RPM_SUMMARY='simple Hello World powered by XULRunner.'

# the category of the app. See /usr/share/doc/rpm/GROUPS
RPM_GROUP=Development/Tools

# keyword license
RPM_LICENSE='MPLv1.1 or GPLv2+ or LGPLv2+'

# url where the source of our app can be found
RPM_URL='http://example.com/myapp/'

# the name of the dummy tar.gz source archive, it will be built by this script.
RPM_SOURCE=${APP_NAME}-${RPM_VERSION}.tar.gz

# If the app need some build dependencies, uncomment the following line and add
# them here, separated by a comma. BUT you shouldn't have to, the logic here is
# that the built should have been done before, this script simply package the
# result.
#RPM_BUILDREQUIRES=

# if the app have other dependencies than Firefox, list them, separated by a
# comma. Important, this list MUST begin by a comma.
RPM_EXTRA_REQUIRES=

# long description of the app for the generated rpm. Can be multiline.
RPM_DESCRIPTION='simple Hello World
Powered by XULRunner.'

LC_TIME_BUFFER=$LC_TIME
export LC_TIME="en_EN.utf8"
RPM_DATE=$(date +"%a %b %e %Y")
export LC_TIME="$LC_TIME_BUFFER"

RPM_MAINTAINER='John Doe <johndoe@example.com>'


#_______________________________________________________________________________
#
# Windows specific
#_______________________________________________________________________________

# the folder which contains the Windows specifics data
WIN_DIR=$APP_DATA_DIR/win

# the version of the app used by some other variables. String.
WIN_VERSION=$APP_VERSION

# comment this line if you don't want to build xal, the launcher in C
OPT_BUILD_WIN_XAL=1

# source directory of xal
XAL_SRC_DIR=$TOOLS_DIR/xal-src

# the rc file used to build xal
WIN_RESOURCE=$WIN_DIR/resource.rc

# If this variable is set, the resource (.rc) file for xal will
# be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,
# @@APP_DISPLAY_NAME@@, and @@WINRES_...@@
# will be replaced by their values. Simply comment the following line to not use
# this feature.
OPT_ADAPT_WINRES=1

WINRES_FILEVERSION='0,1,0,0'
# the FILEVERSION for Windows (<int,int,int,int>) will be deducted from the
# APP_VERSION. Comment this line if you don't want use this feature
OPT_CALC_WINRES_FVERSION=1

WINRES_PRODUCTVERSION='0,1,0,0'
# the PRODUCTVERSION for Windows (<int,int,int,int>) will be deducted from the
# APP_VERSION. Comment this line if you don't want use this feature
OPT_CALC_WINRES_PVERSION=1

WINRES_Comments='Published under the MPL 1.1/GPL 2.0/LGPL 2.1 licenses'
WINRES_CompanyName='John Doe Organization'
WINRES_FileDescription=$APP_DISPLAY_NAME
WINRES_FileVersion=$WIN_VERSION
WINRES_InternalName=$APP_NAME
WINRES_LegalCopyright='(c) 2010 John Doe'
WINRES_ProductName=$APP_DISPLAY_NAME
WINRES_ProductVersion=$WIN_VERSION

# Comment the following line if you don't want to generate the nsis installer
# for Windows
OPT_BUILD_WIN_INSTALLER=1

# the source folder of the nsis scripts used by this script
NSIS_SRC_DIR=$APP_DATA_DIR/nsis

# If this variable is set, the nsis script will
# be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,
# @@APP_DISPLAY_NAME@@, and @@NSIS_...@@
# will be replaced by their values. Simply comment the following line to not use
# this feature.
OPT_ADAPT_NSIS=1

NSIS_PRODUCT_NAME=$APP_DISPLAY_NAME
NSIS_PRODUCT_INTERNAL_NAME=$APP_NAME
NSIS_PRODUCT_VERSION=$WIN_VERSION
NSIS_PRODUCT_WIN_VERSION='1.0.0.0'
# for nsis, the PRODUCTVERSION for Windows (<int,int,int,int>) will be deducted
# from the APP_VERSION. Comment this line if you don't want use this feature
OPT_CALC_NSIS_WINVERSION=1

# The license file text MUST be in the main root app folder, so in $APP_SRC_DIR.
# Otherwise, you can edit the nsis script and adapt it
NSIS_LICENCE_NAME='LICENSE.txt'

# the name of the generated nsis installer
NSIS_INSTALLER_NAME="$APP_DISPLAY_NAME-$APP_VERSION-install.exe"


#_______________________________________________________________________________
#
# Mac OSX specific
#_______________________________________________________________________________

# the folder which contains the Mac OSX specifics data
MAC_DIR=$APP_DATA_DIR/mac

# the simple and basic shell launcher for Mac. Used only for the
# multiplatform tar.gz
MAC_BASIC_LAUNCHER=$MAC_DIR/basic_launcher.sh

# the previous basic launcher will be renamed with this value
# Trick: if the extension is 'command', the file can double-clicked in the Mac
# Finder, then a terminal will be opened and the script will be launched.
MAC_BASIC_LAUNCHER_NAME=${APP_NAME}-mac.command

# Comment the following line if you don't want to generate the Application
# Bundle of the app for Mac.
OPT_BUILD_MAC_BUNDLE=1

# path to the used skeleton of the bundle
DMG_SKELET_DIR=$MAC_DIR/bundle_skelet

# If this variable is set, the nsis script will
# be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,
# @@APP_DISPLAY_NAME@@, and @@MAC_...@@
# will be replaced by their values. Simply comment the following line to not use
# this feature.
OPT_ADAPT_MAC_INFO=1

MAC_BUNDLE_EXECUTABLE=${APP_NAME}-mac.sh
MAC_BUNDLE_INFOSTRING="$APP_DISPLAY_NAME $APP_VERSION"
MAC_BUNDLE_ICONFILE=$APP_NAME
MAC_BUNDLE_IDENTIFIER="net.yourcompany.${APP_NAME}"
MAC_BUNDLE_NAME=$APP_DISPLAY_NAME
MAC_BUNDLE_SHORTVERSIONSTRING=$APP_VERSION
MAC_BUNDLE_VERSION=$APP_VERSION

# with this option, the eventual icon 'myapp.icns' in the Contents/Resources
# folder of the used bundle skelet will be removed, and the icns icon of the
# icons folder will be added to the bundle renamed as MAC_BUNDLE_ICONFILE
MAC_BUNDLE_OVERWRITE_ICON=1

# comment the following line if you don't want to generate a dmg image 
OPT_BUILD_MAC_DMG=1
# comment the following line if you don't want to customize the dmg image 
OPT_CUSTOMIZE_DMG=1
# source folder of the data added to the dmg, to customize it
# Note that you have to recreate manually these customizations files if the
# DMG_VOLNAME is different than 'MyApp', in particular the background image
# will not be displayed. See the Howto.
# Comment the line to disabled this option.
DMG_CUSTOM_DATA_DIR=$MAC_DIR/dmg_extra_data

# the name of the mounted dmg disk image
DMG_VOLNAME="$APP_DISPLAY_NAME"
# the name of the generated dmg
DMG_NAME="$APP_NAME-$APP_VERSION.dmg"

# comment the following line if you don't want to compress the dmg. You need
# the <dmg> program to be able to do that
OPT_COMPRESS_DMG=1
# path to the dmg program, used to compress the dmg.
# see http://github.com/planetbeing/libdmg-hfsplus
# and http://shanemcc.co.uk/libdmg/
TOOL_DMG="$TOOLS_DIR/dmg"


#_______________________________________________________________________________
#
# Multi-platform tar.gz archive (usable on Linux, Windows, Mac OSX)
#_______________________________________________________________________________

# Note that al lot of previous variables (Linux, Windows, and Mac OSX specific)
# will be used to build this archive

# Comment the following line if you don't want to generate multi-platform tar.gz
# Note that if ARCH_DEPENDENT is active, this archive will NOT be built
OPT_BUILD_MULTI_TARGZ=1

# the name of the generated tar.gz
TARGZ_NAME=${APP_NAME}-${APP_VERSION}-multiplatform


#_______________________________________________________________________________

BUILD_CONFIG_INCLUDED=1

To use this script for your own application, in short you have to overwrite all data included in the data folder, image, files... then open and adapt the build_config.sh script to your own.

In detail:

Then, in a terminal:

And you will find into the myapp-src-all/dist/ folder all the created installers.

5.5 Conclusions

Creating XULRunner applications is easy, powerful, and multiplatform, like developing Firefox extensions.

You can use some wizards from mozdev.org to generate a skeleton to begin a new app (be careful, you will have to adapt the chrome registration for Firefox 4, the wizard need apparently to be updated for this part).

We have seen in this how-to some small parts that we need to taking care for a better desktop integration, and that it is relatively easy to create installers, from Linux, for several platforms. You can even reuse the global script, or get inspiration from it, to create these installers for your own application.

Now it's up to you to make some nice apps, I hope this how-to and the global script will help you.
Have fun ;) !

2011-06-15 - Nicolas Martin

You can download all the samples of this chapter 5, the global script and XAL included, in the myapp-src-global.tar.gz archive.

The myapp application, from developer.mozilla.org is in the Public Domain.

The icon used is from the Tango Desktop Project, and is in the Public Domain.

The C launcher XUL App Launcher (XAL) is under the MIT license.

All other added data, and sample files, of this chapter 5, are under Public Domains too.