Joliclic blog - Tag - xul2024-03-28T19:02:12+01:00urn:md5:f3d5d2e191f4802c4356831ca0caec2cDotclearDistribuer votre appli XULRunner - 5 - Synthèse - toutes plateformesurn:md5:247cb744694d9dcdbc90be9549c584ff2011-05-27T19:43:00+02:00joliclicmozilladist_xul_app_frhowtoxul <div class="howto">
<p><em>This post exists also in <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/Distribute-your-XULRunner-app-5-Synthesis-all-platforms">english</a></em></p>
<p><em>
Ce billet fait partie d'une <a href="http://joliclic.free.fr/blog/index.php?tag/dist_xul_app_fr">série</a> sur comment déployer une application XULRunner.
Voir le <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/20/Distribuer-votre-appli-XULRunner-1-Preambule">préambule</a> pour le contexte.
</em></p>
<h3><span>5 - Synthèse - toutes plateformes</span></h3>
<ul>
<li>5.1 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howtofr_dxr_5.1">Changements dans l'appli</a></li>
<li>5.2 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howtofr_dxr_5.2">Données ajoutées</a></li>
<li>5.3 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howtofr_dxr_5.3">Installeurs créés</a></li>
<li>5.4 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howtofr_dxr_5.4">Un script global et réutilisable</a></li>
<li>5.5 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howtofr_dxr_5.5">Conclusions</a></li>
</ul>
<div class="howto-section">
<h4><span>5.1 Changements dans l'appli</span></h4>
<p>
Résumons les changements que nous avons apporté à l'application elle-même,
pour chaque plateforme :
</p>
<ul>
<li>
<h5>Linux</h5>
<ul>
<li>
<p>
Nous avons <strong>ajouté quelques icônes png</strong>
dans le dossier <code>myapp/chrome/icons/default/</code> .
<br />
Ceci permet d'avoir notre icône pour nos fenêtres et dans
la barre de tâche.
<br />
En details, 16x16px, 32x32px et 48x48px.
</p>
<p><em>
Nous pouvons conserver ces changements globalement, ils
n'auront pas d'impact sur les autres plateformes.
</em></p>
</li>
</ul>
</li>
<li>
<h5>Windows</h5>
<ul>
<li>
<p>
De manière similaire à Linux, nous avons
<strong>ajouté quelques icônes .ico</strong> dans le dossier
<code>myapp/chrome/icons/default/</code>, pour les mêmes raisons.
<br />
Et nous avons créer cette icône depuis Linux avec le programme
<code>icotool</code>.
</p>
<p><em>
Nous pouvons conserver ces changements globalement, ils n'auront
pas d'impact sur les autres plateformes.
</em></p>
</li>
</ul>
</li>
<li>
<h5>Mac OSX</h5>
<ul>
<li>
<p>
Nous avons <strong>ajouté un élément menubar principal</strong>
dans toutes nos fenêtres non modales, avec
<strong>quelques entrées avec un <code>id</code> spécifique</strong>.
<br />
Ceci nous permet d'avoir nos propres entrées dans le
Menu principal Mac.
<br />
Ces entrées sont masquées (d'un point de vue XUL) par défaut.
</p>
<p><em>
Nous pouvons conserver ces changements globalement, ils n'auront
pas d'impact sur les autres plateformes, parce ces éléments XUL
sont masqués pour les autres plateformes.
</em></p>
</li>
<li>
<p>
Nous avons <strong>ajouté un peu de code JavaScript</strong>
à notre fichier js principal. Parce que sur un Mac l'appli ne quitte
pas quand on ferme toutes les fenêtres.
</p>
<p><em>
Nous pouvons conserver ces changements globalement, le code
est multi-plateforme. Mieux, nous pouvons l'utiliser également
sur les autres plateformes.
</em></p>
</li>
<li>
<p>
Nous avons <strong>ajouté un nouveau fichier, une fenêtre XUL masquée</strong>.
Cette fenêtre est utilisée pour granir le Menu Mac principal quand toutes
nos fenêtres sont fermées.
</p>
<p><em>
Nous pouvons conserver ces changements globalement, ils n'auront
pas d'impact sur les autres plateformes.
</em></p>
</li>
<li>
<p>
Et nous avons <strong>ajouté une préférence</strong>
(<code>browser.hiddenWindowChromeURL</code>),
pour utiliser la fenêtre cachée précédente.
</p>
<p><em>
Nous pouvons conserver ces changements globalement, ils n'auront
pas d'impact sur les autres plateformes.
</em></p>
</li>
</ul>
</li>
</ul>
<p>
Donc, finalement, ce sont de petits changements, et tous peuvent être ajouté
globalement, car ils ne perturbent pas les plateformes non ciblées.
</p>
</div>
<div class="howto-section">
<h4><span>5.2 Données ajoutées</span></h4>
<p>
Pour une meilleure intégration aux différentes platformes, nous avons ajouté
quelques données supplémentaires :
</p>
<ul>
<li>
<h5>Linux</h5>
<ul>
<li>
<p>
Un <strong>script shell utilisé comme lanceur</strong>,
qui utilise de XULRunner ou de Firefox
<br />
Un lien symbolique vers ce lanceur dans
<code>/usr/bin/</code> est également créé par nos
installeurs.
</p>
</li>
<li>
<p>
Un <strong>fichier .desktop</strong>,
A <strong>.desktop file</strong>, pour l'intégration au bureau,
compris par tous les gestionnaires de fenêtre principaux.
</p>
</li>
<li>
<p>
et <strong>quelques icônes</strong> (png et svg), utilisées
par les fichiers desktop.
</p>
</li>
</ul>
</li>
<li>
<h5>Windows</h5>
<ul>
<li>
<p>
Un <strong>vrai lanceur en C</strong>, en fait
<a href="http://joliclic.free.fr/mozilla/xal/" rel="external">XAL</a>
(XUL App Launcher), qui permet de lancer facilement notre appli
avec Firefox, sans aucune fenêtre noire de commande, et qui
peut être personnalisé avec noytre icône.
<br />
Ce lanceur peut être compilé directement depuis Linux.
</p>
</li>
<li>
<p>
Un script batch comme lanceur est également possible,
mais avec les défauts inhérents.
</p>
</li>
</ul>
</li>
<li>
<h5>Mac OSX</h5>
<ul>
<li>
<p>
Pour cette platforme, nous avons en fait encapsulé
notre appli dans un dossier avec une structure spéciale,
avec quelques données, un <strong>Application Bundle</strong>
</p>
</li>
<li>
<p>
Un <strong>script shell utilisé comme lanceur</strong>,
qui utilise Firefox (en fait un lien symbolique vers lui, placé
dans notre bundle)
</p>
</li>
<li>
<p>
Une<strong>icône icns</strong>, utilisée par le bundle pour
l'exécutable et dans le dock.
<br />
et nous avons créer cette icône depuis Linux avec le programme
<code>png2icns</code>.
</p>
</li>
<li>
<p>
Un fichier <strong>Info.plist</strong> et un fichier <strong>PkgInfo</strong>,
décrivant le bundle.
</p>
</li>
</ul>
</li>
</ul>
</div>
<div class="howto-section">
<h4><span>5.3 Installeurs créés</span></h4>
<ul>
<li>
<h5>Multi-plateformes</h5>
<p>
Pas de réelle intégration au bureau, et avec quelques limitations
importante sur Mac, mais un <strong>tar.gz</strong> fonctionne et est simple.
</p>
</li>
<li>
<h5>Linux</h5>
<p>
Nous avons créé un paquet <strong>deb</strong> pour les distributions
basées sur Debian/Ubuntu, un deb similaire pour <strong>Maemo</strong>,
et un <strong>rpm</strong> pour les distributions basées sur Red Hat/Fedora.
</p>
</li>
<li>
<h5>Windows</h5>
<p>
Nous avons créer un vrai installeur/désinstalleur, directement depuis Linux,
basé sur <strong>NSIS</strong>.
</p>
</li>
<li>
<h5>Mac OSX</h5>
<p>
Le mode de distribution le plus convivial sur Mac, est de créer un
<strong>dmg</strong>. Nous avons pu le créer depuis Linux, et
avec un programme expérimental, de le compresser.
</p>
<p>
Un simple tar.gz a aussi été créé.
</p>
</li>
</ul>
</div>
<div class="howto-section">
<h4><span>5.4 Un script global et réutilisable</span></h4>
<p>
Dans chaque chapitres, nous avons utilisés des scripts bash pour créer
tous nos installeurs et autres données.
<br />
Je vous propose maintenant un script global pour réaliser toutes ces tâches
en une fois.
</p>
<p>
Plus, ce script peut être utilisé pour tout autre application XULRunner,
nous avons juste à modifier un fichier de configuration.
</p>
<p>
Je n'écris ce script ici, vous le trouverez dans l'archive jointe à ce chapitre,
mais voilà le fichier de configuration :
</p>
<pre>#!/bin/bash<br /><br />#_______________________________________________________________________________<br />#<br /># script version: 1.0<br /># date: 2011-05-20<br />#_______________________________________________________________________________<br /><br /># exit the script on errors<br />set -e<br /><br />#_______________________________________________________________________________<br />#<br /># common configuration<br />#_______________________________________________________________________________<br /><br /># The name of the application, will be the name of the executable for example.<br /># It should contain only [a-zA-Z_-.] and no whitespace.<br />APP_NAME=myapp<br /><br /># The name of the application displayed on screen.<br />APP_DISPLAY_NAME=MyApp<br /><br /># The absolute path to the directory containing this script. Don't modify this<br /># variable if you don't know what you are doing.<br />CUR_DIR=$(dirname "$0")<br />CUR_DIR=$(readlink -f -- "$CUR_DIR")<br /><br /># The main folder of the sources (absolute path), containing the app, data,...<br /># Can be defined before, in some other scripts.<br />if [ ! $MAIN_DIR ]; then<br /> MAIN_DIR=$(readlink -f -- "$CUR_DIR/..")<br />fi<br /><br /># The folder where all resulted builds (installers, archives,...) will be copied<br />DIST_DIR=$MAIN_DIR/dist<br /><br /># The temporary folder where all build are done<br />TMP_DIR=$MAIN_DIR/tmp<br /><br /># Folder which contains extra useful programs for the build script<br />TOOLS_DIR=$MAIN_DIR/tools<br /><br /># Folder which contains all build scripts, and the current config file<br />BUILDERS_DIR=$MAIN_DIR/builders<br /><br /># The folder of the real sources of the XULRunner application<br />APP_SRC_DIR=$MAIN_DIR/$APP_NAME<br /><br /># The folder of the extra data of the app (icons,...)<br />APP_DATA_DIR=$MAIN_DIR/data<br /><br /># The folder of the used icons<br /># This folder must contains:<br /># icon16.png, icon22.png, icon26.png, icon32.png, icon40.png, icon48.png,<br /># icon128.png, icon48.svg, icon.ico, icon.icns, icon48.txt<br /># You can find some shell scripts in the data/icons folder to create the ico,<br /># icns et base64 icons.<br />APP_ICON_DIR=$APP_DATA_DIR/icons<br /><br /># The current version of the app<br />APP_VERSION=1.0<br /><br /># You can use a file named version in the main directory too, and uncomment the<br /># following line to set this variable<br />#APP_VERSION=$(cat "$MAIN_DIR/version")<br /><br /># if the app contains some compiled code, and so it's architecture dependent,<br /># uncomment the following line<br />#ARCH_DEPENDENT=1<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Linux specific<br />#_______________________________________________________________________________<br /><br /># the folder which contains the Linux specifics data<br />LINUX_DIR=$APP_DATA_DIR/linux<br /><br /># the desktop file, will be renamed as $APP_NAME.desktop<br />DESKTOP_FILE=$LINUX_DIR/desktop<br /><br /># If this variable is set, the desktop file will be adapted, i.e. the strings<br /># @@APP_VERSION@@, @@APP_NAME@@, @@APP_DISPLAY_NAME@@, and<br /># @@APP_DESKTOP_CATEGORIE@@,..., will be replaced by their values. Simply<br /># comment the following line to not use this feature.<br />OPT_ADAPT_DESKTOP=1<br /><br /># this value is displayed in a tooltip by the desktop<br />APP_DESKTOP_COMMENT="a Hello World XULRunner app"<br /><br /># A value chosen in http://standards.freedesktop.org/menu-spec/latest/apa.html<br /># If you use multiple value, use a semicolon (;) as separator<br /># DON'T forget the last semicolon, even if there is only one value<br />APP_DESKTOP_CATEGORIE='Utility;'<br /><br /># You can use this variable to put extra lines to the Desktop file, for example<br /># a localized comment<br />APP_DESKTOP_EXTRA='Comment[fr]=un Bonjour Monde pour XULRunner.\n'<br /><br /># the generic Linux launcher, a shell script, for our app. Will be renamed as<br /># $APP_NAME.sh<br />LINUX_LAUNCHER=$LINUX_DIR/launcher.sh<br /><br /><br />#_______________________________________________________________________________<br />#<br /># deb package specific<br />#_______________________________________________________________________________<br /><br /># Comment the following line if you don't want to generate a deb package<br />OPT_BUILD_DEB=1<br /><br /># the debian folder used to build our generic deb<br />DEBIAN_DIR=$APP_DATA_DIR/debian<br /><br /># If this variable is set, the files 'changelog', 'control', 'menu',<br /># 'myapp.link', and 'copyright' of the debian folder will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, @@DEB_CONTROL_VERSION@@, @@DEB_CONTROL_SECTION@@,<br /># @@DEB_MENU_SECTION@@, ..., will be replaced by their values. Simply comment<br /># the following line to not use this feature.<br />OPT_ADAPT_DEBIAN=1<br /><br /># The version of the generated deb package.<br />DEB_CONTROL_VERSION=${APP_VERSION}-1<br /><br /># the category of the app. See http://packages.debian.org/en/sid/<br />DEB_CONTROL_SECTION=Utilities<br /><br />DEB_CONTROL_MAINTAINER='John Doe <johndoe@example.com>'<br /><br /># author of the generated deb<br />DEB_CONTROL_AUTHOR=$DEB_CONTROL_MAINTAINER<br /><br />DEB_CONTROL_HOMEPAGE='<http://example.com/myapp/>'<br /><br /># name of the generated package<br />DEB_CONTROL_PACKAGE=$APP_NAME<br /><br /># long description of the app for the generated deb. Can be multiline, see<br /># http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description<br /># for the formatting (in short, add at least a space at the beginning of all<br /># lines apart the first)<br />DEB_CONTROL_DESCRIPTION='simple Hello World.<br /> Powered by XULRunner.'<br /><br /># if the app have other dependencies than Firefox, list them, separated by a<br /># comma. Important, this list MUST begin by a comma.<br />DEB_CONTROL_EXTRA_DEPENDENCIES=<br /><br /># if the app contains some compiled code, and you want to use the<br /># ${shlibs:Depends} variable with debhelper, uncomment the following line<br />#DEB_CONTROL_USE_SHLIBS_DEPENDS=1<br /><br /># the category for the Debian menu, see<br /># http://www.debian.org/doc/packaging-manuals/menu-policy/ch2.html#s2.1<br />DEB_MENU_SECTION='Applications/Programming'<br /><br />DEB_DATE="$(date -R)"<br /><br />DEB_COPYRIGHT='<Copyright (C) 2010 John Doe>'<br /><br /># the short license for the generated deb<br />DEB_SHORT_LICENSE=$(cat "$LINUX_DIR/deb_short_license.txt")<br /><br /><br />#_______________________________________________________________________________<br />#<br /># maemo deb specific<br />#_______________________________________________________________________________<br /><br /># Note that the debian specific variables are used as well to generate the<br /># maemo deb, so they must set.<br /><br /># the APP_NAME specific for Maemo, can be the same as APP_NAME<br />MOBILE_APP_NAME=$APP_NAME<br /><br /># the APP_DISPLAY_NAME specific for Maemo, can be the same as APP_DISPLAY_NAME<br />MOBILE_APP_DISPLAY_NAME=$APP_DISPLAY_NAME<br /><br /># the name of the app for the package. Should be different than APP_NAME,<br /># otherwise there can be collision with the generic deb created by the other<br /># script<br />MAEMO_PKG_APPNAME=${APP_NAME}-mobile<br /><br /># the desktop file for Maemo, will be renamed as $APP_NAME.desktop<br />MAEMO_DESKTOP_FILE=$LINUX_DIR/maemodesktop<br /><br /># the launcher for Maemo, a shell script, for our app. Will be renamed as<br /># $APP_NAME.sh<br />MAEMO_LAUNCHER=$LINUX_DIR/maemolauncher.sh<br /><br /># Comment the following line if you don't want to generate a deb package<br />OPT_BUILD_MAEMO_DEB=1<br /><br /># the debian folder used to build our deb for Maemo<br />MAEMODEBIAN_DIR=$APP_DATA_DIR/maemodebian<br /><br /># the categorie of the app for Maemo. See<br /># http://wiki.maemo.org/Packaging/Guidelines#Sections<br />DEB_CONTROL_SECTION_MAEMO=user/utilities<br /><br /># the name of the generated deb for Maemo.<br /># Should really the same as MAEMO_PKG_APPNAME, see its comment.<br />DEB_CONTROL_PACKAGE_MAEMO=$MAEMO_PKG_APPNAME<br /><br /># the description of the generated deb, especially for Maemo. See the comments<br /># for DEB_CONTROL_DESCRIPTION in this file for formatting.<br />DEB_CONTROL_DESCRIPTION_MAEMO=$DEB_CONTROL_DESCRIPTION<br /><br /># like DEB_CONTROL_EXTRA_DEPENDENCIES, but for Maemo<br />DEB_CONTROL_EXTRA_DEPENDENCIES_MAEMO=<br /><br /># the base64 file (text) of the icon for the Maemo deb.<br /># Can be generated by the script 'build_icon_base64.sh' in the data/icons dir.<br />MAEMO_BASE64_ICON="$APP_ICON_DIR/icon48.txt"<br /><br /><br />#_______________________________________________________________________________<br />#<br /># rpm package specific<br />#_______________________________________________________________________________<br /><br /># Comment the following line if you don't want to generate a rpm package<br />OPT_BUILD_RPM=1<br /><br /># path to the spec file used to generate the rpm<br />RPM_SPEC_FILE=$LINUX_DIR/rpmspec<br /><br /># If this variable is set, the .spec file will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, @@RPM_VERSION@@, @@RPM_RELEASE@@, @@RPM_GROUP@@,<br /># @@RPM_LICENSE@@, @@RPM_URL@@, @@RPM_SOURCE@@, @@RPM_DATE@@, ...<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_RPM=1<br /><br /># version of the app for the spec file: must contain only integer and digit, no<br /># strings like 'beta'<br />RPM_VERSION=$APP_VERSION<br /><br /># the version of the generated rpm, not the app version<br />RPM_RELEASE='1%{?dist}'<br /><br /># the short version of the generated rpm, without any string relative to the<br /># distribution or architecture<br />#RPM_RELEASE_SHORT=1<br />RPM_RELEASE_SHORT=$(echo $RPM_RELEASE | sed "s/%.*$//")<br /><br /># breve summary of the app for the package. One line only.<br />RPM_SUMMARY='simple Hello World powered by XULRunner.'<br /><br /># the category of the app. See /usr/share/doc/rpm/GROUPS<br />RPM_GROUP=Development/Tools<br /><br /># keyword license<br />RPM_LICENSE='MPLv1.1 or GPLv2+ or LGPLv2+'<br /><br /># url where the source of our app can be found<br />RPM_URL='http://example.com/myapp/'<br /><br /># the name of the dummy tar.gz source archive, it will be built by this script.<br />RPM_SOURCE=${APP_NAME}-${RPM_VERSION}.tar.gz<br /><br /># If the app need some build dependencies, uncomment the following line and add<br /># them here, separated by a comma. BUT you shouldn't have to, the logic here is<br /># that the built should have been done before, this script simply package the<br /># result.<br />#RPM_BUILDREQUIRES=<br /><br /># if the app have other dependencies than Firefox, list them, separated by a<br /># comma. Important, this list MUST begin by a comma.<br />RPM_EXTRA_REQUIRES=<br /><br /># long description of the app for the generated rpm. Can be multiline.<br />RPM_DESCRIPTION='simple Hello World<br />Powered by XULRunner.'<br /><br />LC_TIME_BUFFER=$LC_TIME<br />export LC_TIME="en_EN.utf8"<br />RPM_DATE=$(date +"%a %b %e %Y")<br />export LC_TIME="$LC_TIME_BUFFER"<br /><br />RPM_MAINTAINER='John Doe <johndoe@example.com>'<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Windows specific<br />#_______________________________________________________________________________<br /><br /># the folder which contains the Windows specifics data<br />WIN_DIR=$APP_DATA_DIR/win<br /><br /># the version of the app used by some other variables. String.<br />WIN_VERSION=$APP_VERSION<br /><br /># comment this line if you don't want to build xal, the launcher in C<br />OPT_BUILD_WIN_XAL=1<br /><br /># source directory of xal<br />XAL_SRC_DIR=$TOOLS_DIR/xal-src<br /><br /># the rc file used to build xal<br />WIN_RESOURCE=$WIN_DIR/resource.rc<br /><br /># If this variable is set, the resource (.rc) file for xal will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, and @@WINRES_...@@<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_WINRES=1<br /><br />WINRES_FILEVERSION='0,1,0,0'<br /># the FILEVERSION for Windows (<int,int,int,int>) will be deducted from the<br /># APP_VERSION. Comment this line if you don't want use this feature<br />OPT_CALC_WINRES_FVERSION=1<br /><br />WINRES_PRODUCTVERSION='0,1,0,0'<br /># the PRODUCTVERSION for Windows (<int,int,int,int>) will be deducted from the<br /># APP_VERSION. Comment this line if you don't want use this feature<br />OPT_CALC_WINRES_PVERSION=1<br /><br />WINRES_Comments='Published under the MPL 1.1/GPL 2.0/LGPL 2.1 licenses'<br />WINRES_CompanyName='John Doe Organization'<br />WINRES_FileDescription=$APP_DISPLAY_NAME<br />WINRES_FileVersion=$WIN_VERSION<br />WINRES_InternalName=$APP_NAME<br />WINRES_LegalCopyright='(c) 2010 John Doe'<br />WINRES_ProductName=$APP_DISPLAY_NAME<br />WINRES_ProductVersion=$WIN_VERSION<br /><br /># Comment the following line if you don't want to generate the nsis installer<br /># for Windows<br />OPT_BUILD_WIN_INSTALLER=1<br /><br /># the source folder of the nsis scripts used by this script<br />NSIS_SRC_DIR=$APP_DATA_DIR/nsis<br /><br /># If this variable is set, the nsis script will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, and @@NSIS_...@@<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_NSIS=1<br /><br />NSIS_PRODUCT_NAME=$APP_DISPLAY_NAME<br />NSIS_PRODUCT_INTERNAL_NAME=$APP_NAME<br />NSIS_PRODUCT_VERSION=$WIN_VERSION<br />NSIS_PRODUCT_WIN_VERSION='1.0.0.0'<br /># for nsis, the PRODUCTVERSION for Windows (<int,int,int,int>) will be deducted<br /># from the APP_VERSION. Comment this line if you don't want use this feature<br />OPT_CALC_NSIS_WINVERSION=1<br /><br /># The license file text MUST be in the main root app folder, so in $APP_SRC_DIR.<br /># Otherwise, you can edit the nsis script and adapt it<br />NSIS_LICENCE_NAME='LICENSE.txt'<br /><br /># the name of the generated nsis installer<br />NSIS_INSTALLER_NAME="$APP_DISPLAY_NAME-$APP_VERSION-install.exe"<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Mac OSX specific<br />#_______________________________________________________________________________<br /><br /># the folder which contains the Mac OSX specifics data<br />MAC_DIR=$APP_DATA_DIR/mac<br /><br /># the simple and basic shell launcher for Mac. Used only for the<br /># multiplatform tar.gz<br />MAC_BASIC_LAUNCHER=$MAC_DIR/basic_launcher.sh<br /><br /># the previous basic launcher will be renamed with this value<br /># Trick: if the extension is 'command', the file can double-clicked in the Mac<br /># Finder, then a terminal will be opened and the script will be launched.<br />MAC_BASIC_LAUNCHER_NAME=${APP_NAME}-mac.command<br /><br /># Comment the following line if you don't want to generate the Application<br /># Bundle of the app for Mac.<br />OPT_BUILD_MAC_BUNDLE=1<br /><br /># path to the used skeleton of the bundle<br />DMG_SKELET_DIR=$MAC_DIR/bundle_skelet<br /><br /># If this variable is set, the nsis script will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, and @@MAC_...@@<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_MAC_INFO=1<br /><br />MAC_BUNDLE_EXECUTABLE=${APP_NAME}-mac.sh<br />MAC_BUNDLE_INFOSTRING="$APP_DISPLAY_NAME $APP_VERSION"<br />MAC_BUNDLE_ICONFILE=$APP_NAME<br />MAC_BUNDLE_IDENTIFIER="net.yourcompany.${APP_NAME}"<br />MAC_BUNDLE_NAME=$APP_DISPLAY_NAME<br />MAC_BUNDLE_SHORTVERSIONSTRING=$APP_VERSION<br />MAC_BUNDLE_VERSION=$APP_VERSION<br /><br /># with this option, the eventual icon 'myapp.icns' in the Contents/Resources<br /># folder of the used bundle skelet will be removed, and the icns icon of the<br /># icons folder will be added to the bundle renamed as MAC_BUNDLE_ICONFILE<br />MAC_BUNDLE_OVERWRITE_ICON=1<br /><br /># comment the following line if you don't want to generate a dmg image <br />OPT_BUILD_MAC_DMG=1<br /># comment the following line if you don't want to customize the dmg image <br />OPT_CUSTOMIZE_DMG=1<br /># source folder of the data added to the dmg, to customize it<br /># Note that you have to recreate manually these customizations files if the<br /># DMG_VOLNAME is different than 'MyApp', in particular the background image<br /># will not be displayed. See the Howto.<br /># Comment the line to disabled this option.<br />DMG_CUSTOM_DATA_DIR=$MAC_DIR/dmg_extra_data<br /><br /># the name of the mounted dmg disk image<br />DMG_VOLNAME="$APP_DISPLAY_NAME"<br /># the name of the generated dmg<br />DMG_NAME="$APP_NAME-$APP_VERSION.dmg"<br /><br /># comment the following line if you don't want to compress the dmg. You need<br /># the <dmg> program to be able to do that<br />OPT_COMPRESS_DMG=1<br /># path to the dmg program, used to compress the dmg.<br /># see http://github.com/planetbeing/libdmg-hfsplus<br /># and http://shanemcc.co.uk/libdmg/<br />TOOL_DMG="$TOOLS_DIR/dmg"<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Multi-platform tar.gz archive (usable on Linux, Windows, Mac OSX)<br />#_______________________________________________________________________________<br /><br /># Note that al lot of previous variables (Linux, Windows, and Mac OSX specific)<br /># will be used to build this archive<br /><br /># Comment the following line if you don't want to generate multi-platform tar.gz<br /># Note that if ARCH_DEPENDENT is active, this archive will NOT be built<br />OPT_BUILD_MULTI_TARGZ=1<br /><br /># the name of the generated tar.gz<br />TARGZ_NAME=${APP_NAME}-${APP_VERSION}-multiplatform<br /><br /><br />#_______________________________________________________________________________<br /><br />BUILD_CONFIG_INCLUDED=1</pre>
<p>
Pour <strong>utiliser ce script pour votre application</strong>, en raccourci
vous devez remplacer toutes les données du dossier data, images, fichier...
puis ouvrir et adapter le script <code>build_config.sh</code> pour votre appli.
</p>
<p>
<strong>En detail :</strong>
</p>
<ul>
<li>
<p>
remplacer toutes les icônes du dossier <code>data/icons</code>
par les votres.
<br />
Si vous n'avez pas celles aux formats <code>.ico</code> et
<code>.icns</code>, utilisez les scripts <code>build_ico.sh</code> et
<code>build_icns.sh</code> pour les créer. Et utilisez le script
<code>build_base64.sh</code> pour générer l'icône base64 pour le
paquet Maemo.
</p>
</li>
<li>
<p>
Éditez et modifiez le fichier <code>deb_short_license.txt</code>
dans le dossier <code>data/linux</code>.
</p>
</li>
<li>
<p>
Si vous voulez créer un dmg pour Mac OSX et le personnaliser, vous
devez recréer les données utilisées, en particulier le fichier
<code>.DS_STORE</code>, et remplacer celles du dossier
<code>data/mac/dmg_extra_data</code> par les votres.
Faites attention, il y a quelques fichiers cachés dans ce dossier
sur Linux, parce que leur noms commencent par un point (.)
<em>(CTRL+H in Nautilus to show them ;)</em>.
</p>
</li>
<li>
<p>
Assurez vous d'avoir un fichier de licence dans le dossier principal
de vottre appli, c'est nécessaire pour créer l'installeur Windows, et
c'est vraiment une bonne pratique.
</p>
</li>
<li>
<p>
Toutes les autres données peuvent etre utilisées telles quelles,
parce que leurs contenus sont parséx par les scripts et utilise
le fichier de config, mais vous aussi pouvez les éditer et les modifier
si vous le souhaitez.
</p>
</li>
<li>
<p>
Si vous voulez créer un dmg compressé pour Mac OSX, vous devez
télécharger le programme libdmg depuis la
<a href="https://github.com/planetbeing/libdmg-hfsplus" rel="external">page de l'auteur</a>
ou cette <a href="http://shanemcc.co.uk/libdmg/" rel="external">version modifiée</a>,
et le placer dans le dossier <code>tools</code>.
</p>
</li>
<li>
Enfin, éditer et adapter le fichier <strong><code>config.sh</code></strong>
dans le dossier <code>builders</code>.
<br />
Les paramètres les plus importants sont
<code>APP_NAME</code> et <code>APP_DISPLAY_NAME</code>, beaucoup d'autres paramètres
utilisent leurs valeurs.
<br />
Et bien le chemin vers les sources de votre appli, çà peut être une
bonne idée de les placer au même niveau que l'exemple myapp,
mais ce n'est pas obligatoire.
<br />
Remplacer bien sûr toutes les chaînes spécifiques à l'exemple myapp.
</li>
</ul>
<p>Puis dans un terminal :</p>
<ul class="code-xterm">
<li><pre>cd myapp-src-global</pre></li>
<li><pre>sh ./build_all.sh</pre></li>
</ul>
<p>
Et vous trouverez alors dans le dossier <code>myapp-src-all/dist/</code>
tous les installeurs créés.
</p>
</div>
<div class="howto-section">
<h4><span>5.5 Conclusions</span></h4>
<p>
Créer des applications XULRunner est facile, puissant et multi-plateforme,
comme développer des extensions Firefox.
</p>
<p>
Vous pouvez utiliser
<a href="http://www.mozdev.org/projects/wizard/" rel="external">des assistants</a>
sur mozdev.org pour générer des squelettes pour commencer une nouvelle appli
(attention vous devrez adapter
<a href="https://developer.mozilla.org/en/chrome_registration" rel="external">enregistrement chrome</a>
pour Firefox 4, l'assistant a besoin apparemment d'être mis à jour pour cette partie).
</p>
<p>
Nous avons vu dans ce tutoriel quelques petites parties auxquelles nous
devons faire attention pour une meilleure intégration au bureau, et qu'il
est relativement simple de créer des installeurs, depuis Linux, pour différentes
plateformes cibles.
Vous pouvez même réutiliser le script global, ou vous en inspirer, pour
créer ces installeurs pour votre application.
</p>
<p>
Maintenant c'est à vous de faire de jolies applis, j'espère que ce tuto
et le script global vous aideront.
<br />
Amusez vous bien ;) !
</p>
</div>
<div class="howto-navigator-bottom">
<div class="howto-previous"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribuer-votre-appli-XULRunner-4-Mac-OSX" title="Aller au chapitre 4">< chapitre précédent</a></div>
<div class="clearer"> </div>
</div>
<div class="howto-author">
<p>Nicolas Martin</p>
</div>
<div class="howto-download-section">
<p>
Vous pouvez télécharger tous les exemples de ce chapitre 5, le script
global et XAL inclus, dans l'archive
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/dl.php?id=current_global" class="howto-download-archive">
myapp-src-global.tar.gz
</a>.
</p>
</div>
<div class="howto-homepage-section">
<p>
Ou retrouver l'intégralité de ce tuto dans
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/" class="howto-homepage-link">
sa page dédiée</a>, disponible en
téléchargement avec tous les exemples compris.
</p>
</div>
<div class="howto-credits">
<p><em>
L'application myapp, de
<a href="https://developer.mozilla.org/en/getting_started_with_xulrunner" rel="external">developer.mozilla.org</a>,
est dans le <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domaine Public</a>.
</em></p>
<p><em>
L'icône utilisée est issue du
<a href="http://tango.freedesktop.org" rel="external">Tango Desktop Project</a>,
et est dans le <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domaine Public</a>.
</em></p>
<p><em>
le lanceur C
<a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">XUL App Launcher</a> (XAL)
est sous <a href="http://www.opensource.org/licenses/mit-license.php" rel="external">licence MIT</a>.
</em></p>
<p><em>
Toutes les autres données ajoutées, et les fichiers en exemple, de ce chapitre 3, sont dans le
<a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domain Public</a> également.
</em></p>
</div>
<div class="howto-footer">
<p>
Le contenu de cet article est sous les termes de la licence
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>
</p>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width: 0;" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a>
</div>
</div>http://joliclic.free.fr/blog/index.php?post/2011/05/27/Distribuer-votre-appli-XULRunner-5-Synthese-toutes-plateformes#comment-formhttp://joliclic.free.fr/blog/index.php?feed/atom/comments/86Distribute your XULRunner app - 5 - Synthesis - all platformsurn:md5:93a07dcf858c99ef46471bc31e7b9f882011-05-27T19:40:00+02:00joliclicmozilladist_xul_app_enhowtoxul <div class="howto">
<p><em>Ce billet existe aussi en <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/Distribuer-votre-appli-XULRunner-5-Synthese-toutes-plateformes">français</a></em></p>
<p><em>
This post is part of a <a href="http://joliclic.free.fr/blog/index.php?tag/dist_xul_app_en">series</a> about how to package a XULRunner application.<br />
See the <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/20/Distribute-your-XULRunner-app-1-Preamble">preamble</a> for the context case.
</em></p>
<h3><span>5 - Synthesis - all platforms</span></h3>
<ul>
<li>5.1 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howto_dxr_5.1">Changes to the app</a></li>
<li>5.2 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howto_dxr_5.2">Added data</a></li>
<li>5.3 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howto_dxr_5.3">Created installers</a></li>
<li>5.4 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howto_dxr_5.4">A global and reusable script</a></li>
<li>5.5 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/#howto_dxr_5.5">Conclusions</a></li>
</ul>
<div class="howto-section">
<h4><span>5.1 Changes to the app</span></h4>
<p>
Let summarize the changes we have made to the application itself, for
each platform:
</p>
<ul>
<li>
<h5>Linux</h5>
<ul>
<li>
<p>
We have <strong>added some png icons</strong> into
the <code>myapp/chrome/icons/default/</code> folder.
<br />
This allows to have our icon for our windows and
taskbar.
<br />
In details, 16x16px, 32x32px and 48x48px.
</p>
<p><em>
We can keep this change globally, it will have no impact
on other platforms.
</em></p>
</li>
</ul>
</li>
<li>
<h5>Windows</h5>
<ul>
<li>
<p>
Similarly as for Linux,
we have <strong>added some .ico icon</strong> into
the <code>myapp/chrome/icons/default/</code> folder, for the
same reasons.
<br />
And we have built this icon from Linux with the <code>icotool</code> program.
</p>
<p><em>
We can keep this change globally, it will have no impact
on other platforms.
</em></p>
</li>
</ul>
</li>
<li>
<h5>Mac OSX</h5>
<ul>
<li>
<p>
We have <strong>added a main menubar</strong> in all our
non-modal windows, with <strong>some entries with a special
<code>id</code></strong>.
<br />
This allows to have our owns entries in the Main Menu of Mac.
<br />
These entries are hidden (in XUL sens) by default.
</p>
<p><em>
We can keep this change globally, it will have no impact
on other platforms, because these XUL elements are hidden
for other platforms.
</em></p>
</li>
<li>
<p>
We have <strong>added some JavaScript code</strong> to our main js file,
to allow quitting properly our app. Because on a Mac the app
doesn't quit when we close all windows.
</p>
<p><em>
We can keep this change globally, this code is multi platform.
More, we can use it on the other platform as well.
</em></p>
</li>
<li>
<p>
We have <strong>added a new file, an hidden XUL window</strong>.
This window is used to populate the Main Mac Menu when
all our windows are closed.
</p>
<p><em>
We can keep this change globally, it will have no impact
on other platforms.
</em></p>
</li>
<li>
<p>
And we have <strong>added a new preference</strong>
(<code>browser.hiddenWindowChromeURL</code>),
to use the previous hidden window.
</p>
<p><em>
We can keep this change globally, it will have no impact
on other platforms.
</em></p>
</li>
</ul>
</li>
</ul>
<p>
So, finally these are small changes, and all of them can be added globally,
because they don't disturb the untargeted platforms.
</p>
</div>
<div class="howto-section">
<h4><span>5.2 Added data</span></h4>
<p>
For a better integration to each platforms, we have added some specific data:
</p>
<ul>
<li>
<h5>Linux</h5>
<ul>
<li>
<p>
A <strong>shell script used as launcher</strong>,
which relies on XULRunner or Firefox.
<br />
A symbolic link to this launcher into
<code>/usr/bin/</code> is also created by our
installers.
</p>
</li>
<li>
<p>
A <strong>.desktop file</strong>, for desktop integration,
understood by all main Window Manager.
</p>
</li>
<li>
<p>
And <strong>some icons</strong> (png and svg), used by
the desktop files.
</p>
</li>
</ul>
</li>
<li>
<h5>Windows</h5>
<ul>
<li>
<p>
A <strong>real launcher in C</strong>, in fact
<a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">XAL</a>
(XUL App Launcher), which allows to easily launch our app
with Firefox, without any black command window, and can
be customized with our icon.
<br />
This launcher can be compiled directly from Linux.
</p>
</li>
<li>
<p>
A batch script as launcher, is possible too, but with
some inherent defaults.
</p>
</li>
</ul>
</li>
<li>
<h5>Mac OSX</h5>
<ul>
<li>
<p>
For this platform, we have in fact encapsulated our app
into a special folder structure with some data,
an <strong>Application Bundle</strong>
</p>
</li>
<li>
<p>
A <strong>shell script used as launcher</strong>, which
uses Firefox (in fact a symbolic link to it, placed in
our bundle)
</p>
</li>
<li>
<p>
A <strong>icns icon</strong>, used by the bundle for the
executable and in the dock.
<br />
And we have built this icon from Linux with the
<code>png2icns</code> program.
</p>
</li>
<li>
<p>
A <strong>Info.plist</strong> file and a
<strong>PkgInfo</strong> file, describing the bundle.
</p>
</li>
</ul>
</li>
</ul>
</div>
<div class="howto-section">
<h4><span>5.3 Created installers</span></h4>
<ul>
<li>
<h5>Multi-platform</h5>
<p>
No real desktop integration, and with some real limitations on Mac,
but a <strong>tar.gz</strong> works and is simple.
</p>
</li>
<li>
<h5>Linux</h5>
<p>
We have created a <strong>deb</strong> package for Debian/Ubuntu
based distributions, a similar deb for <strong>Maemo</strong>,
and a <strong>rpm</strong> for Red Hat/Fedora based distributions.
</p>
</li>
<li>
<h5>Windows</h5>
<p>
We have built a real installer/uninstaller, directly from Linux,
based on <strong>NSIS</strong>.
</p>
</li>
<li>
<h5>Mac OSX</h5>
<p>
The easiest distribution method on Mac, is to create a
<strong>dmg</strong>. We have been able to build it from Linux,
and with an experimental program, to compress it.
</p>
<p>
A simple tar.gz was created too.
</p>
</li>
</ul>
</div>
<div class="howto-section">
<h4><span>5.4 A global and reusable script</span></h4>
<p>
In each chapters, we have used some dedicated bash scripts to create all
our installers and other data.
<br />
I propose now a global script to performs all the tasks in one shot.
</p>
<p>
More, this script can be used with any other XULRunner application, we
only have to adapt a config file.
</p>
<p>
I don't write this script here, you'll find it in the joined archive of
this chapter, but here's the config file:
</p>
<pre>#!/bin/bash<br /><br />#_______________________________________________________________________________<br />#<br /># script version: 1.0<br /># date: 2011-05-20<br />#_______________________________________________________________________________<br /><br /># exit the script on errors<br />set -e<br /><br />#_______________________________________________________________________________<br />#<br /># common configuration<br />#_______________________________________________________________________________<br /><br /># The name of the application, will be the name of the executable for example.<br /># It should contain only [a-zA-Z_-.] and no whitespace.<br />APP_NAME=myapp<br /><br /># The name of the application displayed on screen.<br />APP_DISPLAY_NAME=MyApp<br /><br /># The absolute path to the directory containing this script. Don't modify this<br /># variable if you don't know what you are doing.<br />CUR_DIR=$(dirname "$0")<br />CUR_DIR=$(readlink -f -- "$CUR_DIR")<br /><br /># The main folder of the sources (absolute path), containing the app, data,...<br /># Can be defined before, in some other scripts.<br />if [ ! $MAIN_DIR ]; then<br /> MAIN_DIR=$(readlink -f -- "$CUR_DIR/..")<br />fi<br /><br /># The folder where all resulted builds (installers, archives,...) will be copied<br />DIST_DIR=$MAIN_DIR/dist<br /><br /># The temporary folder where all build are done<br />TMP_DIR=$MAIN_DIR/tmp<br /><br /># Folder which contains extra useful programs for the build script<br />TOOLS_DIR=$MAIN_DIR/tools<br /><br /># Folder which contains all build scripts, and the current config file<br />BUILDERS_DIR=$MAIN_DIR/builders<br /><br /># The folder of the real sources of the XULRunner application<br />APP_SRC_DIR=$MAIN_DIR/$APP_NAME<br /><br /># The folder of the extra data of the app (icons,...)<br />APP_DATA_DIR=$MAIN_DIR/data<br /><br /># The folder of the used icons<br /># This folder must contains:<br /># icon16.png, icon22.png, icon26.png, icon32.png, icon40.png, icon48.png,<br /># icon128.png, icon48.svg, icon.ico, icon.icns, icon48.txt<br /># You can find some shell scripts in the data/icons folder to create the ico,<br /># icns et base64 icons.<br />APP_ICON_DIR=$APP_DATA_DIR/icons<br /><br /># The current version of the app<br />APP_VERSION=1.0<br /><br /># You can use a file named version in the main directory too, and uncomment the<br /># following line to set this variable<br />#APP_VERSION=$(cat "$MAIN_DIR/version")<br /><br /># if the app contains some compiled code, and so it's architecture dependent,<br /># uncomment the following line<br />#ARCH_DEPENDENT=1<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Linux specific<br />#_______________________________________________________________________________<br /><br /># the folder which contains the Linux specifics data<br />LINUX_DIR=$APP_DATA_DIR/linux<br /><br /># the desktop file, will be renamed as $APP_NAME.desktop<br />DESKTOP_FILE=$LINUX_DIR/desktop<br /><br /># If this variable is set, the desktop file will be adapted, i.e. the strings<br /># @@APP_VERSION@@, @@APP_NAME@@, @@APP_DISPLAY_NAME@@, and<br /># @@APP_DESKTOP_CATEGORIE@@,..., will be replaced by their values. Simply<br /># comment the following line to not use this feature.<br />OPT_ADAPT_DESKTOP=1<br /><br /># this value is displayed in a tooltip by the desktop<br />APP_DESKTOP_COMMENT="a Hello World XULRunner app"<br /><br /># A value chosen in http://standards.freedesktop.org/menu-spec/latest/apa.html<br /># If you use multiple value, use a semicolon (;) as separator<br /># DON'T forget the last semicolon, even if there is only one value<br />APP_DESKTOP_CATEGORIE='Utility;'<br /><br /># You can use this variable to put extra lines to the Desktop file, for example<br /># a localized comment<br />APP_DESKTOP_EXTRA='Comment[fr]=un Bonjour Monde pour XULRunner.\n'<br /><br /># the generic Linux launcher, a shell script, for our app. Will be renamed as<br /># $APP_NAME.sh<br />LINUX_LAUNCHER=$LINUX_DIR/launcher.sh<br /><br /><br />#_______________________________________________________________________________<br />#<br /># deb package specific<br />#_______________________________________________________________________________<br /><br /># Comment the following line if you don't want to generate a deb package<br />OPT_BUILD_DEB=1<br /><br /># the debian folder used to build our generic deb<br />DEBIAN_DIR=$APP_DATA_DIR/debian<br /><br /># If this variable is set, the files 'changelog', 'control', 'menu',<br /># 'myapp.link', and 'copyright' of the debian folder will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, @@DEB_CONTROL_VERSION@@, @@DEB_CONTROL_SECTION@@,<br /># @@DEB_MENU_SECTION@@, ..., will be replaced by their values. Simply comment<br /># the following line to not use this feature.<br />OPT_ADAPT_DEBIAN=1<br /><br /># The version of the generated deb package.<br />DEB_CONTROL_VERSION=${APP_VERSION}-1<br /><br /># the category of the app. See http://packages.debian.org/en/sid/<br />DEB_CONTROL_SECTION=Utilities<br /><br />DEB_CONTROL_MAINTAINER='John Doe <johndoe@example.com>'<br /><br /># author of the generated deb<br />DEB_CONTROL_AUTHOR=$DEB_CONTROL_MAINTAINER<br /><br />DEB_CONTROL_HOMEPAGE='<http://example.com/myapp/>'<br /><br /># name of the generated package<br />DEB_CONTROL_PACKAGE=$APP_NAME<br /><br /># long description of the app for the generated deb. Can be multiline, see<br /># http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description<br /># for the formatting (in short, add at least a space at the beginning of all<br /># lines apart the first)<br />DEB_CONTROL_DESCRIPTION='simple Hello World.<br /> Powered by XULRunner.'<br /><br /># if the app have other dependencies than Firefox, list them, separated by a<br /># comma. Important, this list MUST begin by a comma.<br />DEB_CONTROL_EXTRA_DEPENDENCIES=<br /><br /># if the app contains some compiled code, and you want to use the<br /># ${shlibs:Depends} variable with debhelper, uncomment the following line<br />#DEB_CONTROL_USE_SHLIBS_DEPENDS=1<br /><br /># the category for the Debian menu, see<br /># http://www.debian.org/doc/packaging-manuals/menu-policy/ch2.html#s2.1<br />DEB_MENU_SECTION='Applications/Programming'<br /><br />DEB_DATE="$(date -R)"<br /><br />DEB_COPYRIGHT='<Copyright (C) 2010 John Doe>'<br /><br /># the short license for the generated deb<br />DEB_SHORT_LICENSE=$(cat "$LINUX_DIR/deb_short_license.txt")<br /><br /><br />#_______________________________________________________________________________<br />#<br /># maemo deb specific<br />#_______________________________________________________________________________<br /><br /># Note that the debian specific variables are used as well to generate the<br /># maemo deb, so they must set.<br /><br /># the APP_NAME specific for Maemo, can be the same as APP_NAME<br />MOBILE_APP_NAME=$APP_NAME<br /><br /># the APP_DISPLAY_NAME specific for Maemo, can be the same as APP_DISPLAY_NAME<br />MOBILE_APP_DISPLAY_NAME=$APP_DISPLAY_NAME<br /><br /># the name of the app for the package. Should be different than APP_NAME,<br /># otherwise there can be collision with the generic deb created by the other<br /># script<br />MAEMO_PKG_APPNAME=${APP_NAME}-mobile<br /><br /># the desktop file for Maemo, will be renamed as $APP_NAME.desktop<br />MAEMO_DESKTOP_FILE=$LINUX_DIR/maemodesktop<br /><br /># the launcher for Maemo, a shell script, for our app. Will be renamed as<br /># $APP_NAME.sh<br />MAEMO_LAUNCHER=$LINUX_DIR/maemolauncher.sh<br /><br /># Comment the following line if you don't want to generate a deb package<br />OPT_BUILD_MAEMO_DEB=1<br /><br /># the debian folder used to build our deb for Maemo<br />MAEMODEBIAN_DIR=$APP_DATA_DIR/maemodebian<br /><br /># the categorie of the app for Maemo. See<br /># http://wiki.maemo.org/Packaging/Guidelines#Sections<br />DEB_CONTROL_SECTION_MAEMO=user/utilities<br /><br /># the name of the generated deb for Maemo.<br /># Should really the same as MAEMO_PKG_APPNAME, see its comment.<br />DEB_CONTROL_PACKAGE_MAEMO=$MAEMO_PKG_APPNAME<br /><br /># the description of the generated deb, especially for Maemo. See the comments<br /># for DEB_CONTROL_DESCRIPTION in this file for formatting.<br />DEB_CONTROL_DESCRIPTION_MAEMO=$DEB_CONTROL_DESCRIPTION<br /><br /># like DEB_CONTROL_EXTRA_DEPENDENCIES, but for Maemo<br />DEB_CONTROL_EXTRA_DEPENDENCIES_MAEMO=<br /><br /># the base64 file (text) of the icon for the Maemo deb.<br /># Can be generated by the script 'build_icon_base64.sh' in the data/icons dir.<br />MAEMO_BASE64_ICON="$APP_ICON_DIR/icon48.txt"<br /><br /><br />#_______________________________________________________________________________<br />#<br /># rpm package specific<br />#_______________________________________________________________________________<br /><br /># Comment the following line if you don't want to generate a rpm package<br />OPT_BUILD_RPM=1<br /><br /># path to the spec file used to generate the rpm<br />RPM_SPEC_FILE=$LINUX_DIR/rpmspec<br /><br /># If this variable is set, the .spec file will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, @@RPM_VERSION@@, @@RPM_RELEASE@@, @@RPM_GROUP@@,<br /># @@RPM_LICENSE@@, @@RPM_URL@@, @@RPM_SOURCE@@, @@RPM_DATE@@, ...<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_RPM=1<br /><br /># version of the app for the spec file: must contain only integer and digit, no<br /># strings like 'beta'<br />RPM_VERSION=$APP_VERSION<br /><br /># the version of the generated rpm, not the app version<br />RPM_RELEASE='1%{?dist}'<br /><br /># the short version of the generated rpm, without any string relative to the<br /># distribution or architecture<br />#RPM_RELEASE_SHORT=1<br />RPM_RELEASE_SHORT=$(echo $RPM_RELEASE | sed "s/%.*$//")<br /><br /># breve summary of the app for the package. One line only.<br />RPM_SUMMARY='simple Hello World powered by XULRunner.'<br /><br /># the category of the app. See /usr/share/doc/rpm/GROUPS<br />RPM_GROUP=Development/Tools<br /><br /># keyword license<br />RPM_LICENSE='MPLv1.1 or GPLv2+ or LGPLv2+'<br /><br /># url where the source of our app can be found<br />RPM_URL='http://example.com/myapp/'<br /><br /># the name of the dummy tar.gz source archive, it will be built by this script.<br />RPM_SOURCE=${APP_NAME}-${RPM_VERSION}.tar.gz<br /><br /># If the app need some build dependencies, uncomment the following line and add<br /># them here, separated by a comma. BUT you shouldn't have to, the logic here is<br /># that the built should have been done before, this script simply package the<br /># result.<br />#RPM_BUILDREQUIRES=<br /><br /># if the app have other dependencies than Firefox, list them, separated by a<br /># comma. Important, this list MUST begin by a comma.<br />RPM_EXTRA_REQUIRES=<br /><br /># long description of the app for the generated rpm. Can be multiline.<br />RPM_DESCRIPTION='simple Hello World<br />Powered by XULRunner.'<br /><br />LC_TIME_BUFFER=$LC_TIME<br />export LC_TIME="en_EN.utf8"<br />RPM_DATE=$(date +"%a %b %e %Y")<br />export LC_TIME="$LC_TIME_BUFFER"<br /><br />RPM_MAINTAINER='John Doe <johndoe@example.com>'<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Windows specific<br />#_______________________________________________________________________________<br /><br /># the folder which contains the Windows specifics data<br />WIN_DIR=$APP_DATA_DIR/win<br /><br /># the version of the app used by some other variables. String.<br />WIN_VERSION=$APP_VERSION<br /><br /># comment this line if you don't want to build xal, the launcher in C<br />OPT_BUILD_WIN_XAL=1<br /><br /># source directory of xal<br />XAL_SRC_DIR=$TOOLS_DIR/xal-src<br /><br /># the rc file used to build xal<br />WIN_RESOURCE=$WIN_DIR/resource.rc<br /><br /># If this variable is set, the resource (.rc) file for xal will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, and @@WINRES_...@@<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_WINRES=1<br /><br />WINRES_FILEVERSION='0,1,0,0'<br /># the FILEVERSION for Windows (<int,int,int,int>) will be deducted from the<br /># APP_VERSION. Comment this line if you don't want use this feature<br />OPT_CALC_WINRES_FVERSION=1<br /><br />WINRES_PRODUCTVERSION='0,1,0,0'<br /># the PRODUCTVERSION for Windows (<int,int,int,int>) will be deducted from the<br /># APP_VERSION. Comment this line if you don't want use this feature<br />OPT_CALC_WINRES_PVERSION=1<br /><br />WINRES_Comments='Published under the MPL 1.1/GPL 2.0/LGPL 2.1 licenses'<br />WINRES_CompanyName='John Doe Organization'<br />WINRES_FileDescription=$APP_DISPLAY_NAME<br />WINRES_FileVersion=$WIN_VERSION<br />WINRES_InternalName=$APP_NAME<br />WINRES_LegalCopyright='(c) 2010 John Doe'<br />WINRES_ProductName=$APP_DISPLAY_NAME<br />WINRES_ProductVersion=$WIN_VERSION<br /><br /># Comment the following line if you don't want to generate the nsis installer<br /># for Windows<br />OPT_BUILD_WIN_INSTALLER=1<br /><br /># the source folder of the nsis scripts used by this script<br />NSIS_SRC_DIR=$APP_DATA_DIR/nsis<br /><br /># If this variable is set, the nsis script will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, and @@NSIS_...@@<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_NSIS=1<br /><br />NSIS_PRODUCT_NAME=$APP_DISPLAY_NAME<br />NSIS_PRODUCT_INTERNAL_NAME=$APP_NAME<br />NSIS_PRODUCT_VERSION=$WIN_VERSION<br />NSIS_PRODUCT_WIN_VERSION='1.0.0.0'<br /># for nsis, the PRODUCTVERSION for Windows (<int,int,int,int>) will be deducted<br /># from the APP_VERSION. Comment this line if you don't want use this feature<br />OPT_CALC_NSIS_WINVERSION=1<br /><br /># The license file text MUST be in the main root app folder, so in $APP_SRC_DIR.<br /># Otherwise, you can edit the nsis script and adapt it<br />NSIS_LICENCE_NAME='LICENSE.txt'<br /><br /># the name of the generated nsis installer<br />NSIS_INSTALLER_NAME="$APP_DISPLAY_NAME-$APP_VERSION-install.exe"<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Mac OSX specific<br />#_______________________________________________________________________________<br /><br /># the folder which contains the Mac OSX specifics data<br />MAC_DIR=$APP_DATA_DIR/mac<br /><br /># the simple and basic shell launcher for Mac. Used only for the<br /># multiplatform tar.gz<br />MAC_BASIC_LAUNCHER=$MAC_DIR/basic_launcher.sh<br /><br /># the previous basic launcher will be renamed with this value<br /># Trick: if the extension is 'command', the file can double-clicked in the Mac<br /># Finder, then a terminal will be opened and the script will be launched.<br />MAC_BASIC_LAUNCHER_NAME=${APP_NAME}-mac.command<br /><br /># Comment the following line if you don't want to generate the Application<br /># Bundle of the app for Mac.<br />OPT_BUILD_MAC_BUNDLE=1<br /><br /># path to the used skeleton of the bundle<br />DMG_SKELET_DIR=$MAC_DIR/bundle_skelet<br /><br /># If this variable is set, the nsis script will<br /># be adapted, i.e. the strings @@APP_VERSION@@, @@APP_NAME@@,<br /># @@APP_DISPLAY_NAME@@, and @@MAC_...@@<br /># will be replaced by their values. Simply comment the following line to not use<br /># this feature.<br />OPT_ADAPT_MAC_INFO=1<br /><br />MAC_BUNDLE_EXECUTABLE=${APP_NAME}-mac.sh<br />MAC_BUNDLE_INFOSTRING="$APP_DISPLAY_NAME $APP_VERSION"<br />MAC_BUNDLE_ICONFILE=$APP_NAME<br />MAC_BUNDLE_IDENTIFIER="net.yourcompany.${APP_NAME}"<br />MAC_BUNDLE_NAME=$APP_DISPLAY_NAME<br />MAC_BUNDLE_SHORTVERSIONSTRING=$APP_VERSION<br />MAC_BUNDLE_VERSION=$APP_VERSION<br /><br /># with this option, the eventual icon 'myapp.icns' in the Contents/Resources<br /># folder of the used bundle skelet will be removed, and the icns icon of the<br /># icons folder will be added to the bundle renamed as MAC_BUNDLE_ICONFILE<br />MAC_BUNDLE_OVERWRITE_ICON=1<br /><br /># comment the following line if you don't want to generate a dmg image <br />OPT_BUILD_MAC_DMG=1<br /># comment the following line if you don't want to customize the dmg image <br />OPT_CUSTOMIZE_DMG=1<br /># source folder of the data added to the dmg, to customize it<br /># Note that you have to recreate manually these customizations files if the<br /># DMG_VOLNAME is different than 'MyApp', in particular the background image<br /># will not be displayed. See the Howto.<br /># Comment the line to disabled this option.<br />DMG_CUSTOM_DATA_DIR=$MAC_DIR/dmg_extra_data<br /><br /># the name of the mounted dmg disk image<br />DMG_VOLNAME="$APP_DISPLAY_NAME"<br /># the name of the generated dmg<br />DMG_NAME="$APP_NAME-$APP_VERSION.dmg"<br /><br /># comment the following line if you don't want to compress the dmg. You need<br /># the <dmg> program to be able to do that<br />OPT_COMPRESS_DMG=1<br /># path to the dmg program, used to compress the dmg.<br /># see http://github.com/planetbeing/libdmg-hfsplus<br /># and http://shanemcc.co.uk/libdmg/<br />TOOL_DMG="$TOOLS_DIR/dmg"<br /><br /><br />#_______________________________________________________________________________<br />#<br /># Multi-platform tar.gz archive (usable on Linux, Windows, Mac OSX)<br />#_______________________________________________________________________________<br /><br /># Note that al lot of previous variables (Linux, Windows, and Mac OSX specific)<br /># will be used to build this archive<br /><br /># Comment the following line if you don't want to generate multi-platform tar.gz<br /># Note that if ARCH_DEPENDENT is active, this archive will NOT be built<br />OPT_BUILD_MULTI_TARGZ=1<br /><br /># the name of the generated tar.gz<br />TARGZ_NAME=${APP_NAME}-${APP_VERSION}-multiplatform<br /><br /><br />#_______________________________________________________________________________<br /><br />BUILD_CONFIG_INCLUDED=1</pre>
<p>
To <strong>use this script for your own application</strong>, in short
you have to overwrite all data included in the data folder, image,
files... then open and adapt the <code>build_config.sh</code> script to
your own.
</p>
<p>
<strong>In detail:</strong>
</p>
<ul>
<li>
<p>
replace all icons in the <code>data/icons</code> folder by your owns.
<br />
If you don't have the <code>.ico</code> and <code>.icns</code>
ones, use the scripts <code>build_ico.sh</code> and
<code>build_icns.sh</code> to build them. And use the
<code>build_base64.sh</code> script to generate the base64 icon
for the Maemo package.
</p>
</li>
<li>
<p>
Edit and modify the file <code>deb_short_license.txt</code> in
the <code>data/linux</code> folder.
</p>
</li>
<li>
<p>
If you want to create a dmg for Mac OSX and customize it, you should
recreate the used data, in particular the <code>.DS_STORE</code> file,
and overwrite them in the <code>data/mac/dmg_extra_data</code>
folder. Be careful, there're some hidden files in this folder on Linux,
because their names begin with a dot (.)
<em>(CTRL+H in Nautilus to show them ;)</em>.
</p>
</li>
<li>
<p>
Be sure to have a license file into the main folder of your app, this
is required to build the Windows installer, and this is really a good
practice.
</p>
</li>
<li>
<p>
All other data could be used as is, because their contents will
parsed by the scripts and use the config file, but you can edit
and change them as well.
</p>
</li>
<li>
<p>
If you want to create a compressed dmg for Mac OSX, you have to
download the libdmg program from the
<a href="https://github.com/planetbeing/libdmg-hfsplus" rel="external">author page</a>
or this <a href="http://shanemcc.co.uk/libdmg/" rel="external">modified version</a>,
and place it in the <code>tools</code> folder.
</p>
</li>
<li>
Finally, edit and adapt the <strong><code>config.sh</code></strong>
file in the <code>builders</code> folder.
<br />
The most important parameters
are <code>APP_NAME</code> and <code>APP_DISPLAY_NAME</code>, a lot of
others parameters use them.
<br />
And of course the path to the sources of your app,
it could be a good idea to place them at the same level of the myapp
example, but that's not required.
<br />
Replace of course all strings specifics to the myapp example.
</li>
</ul>
<p>Then, in a terminal:</p>
<ul class="code-xterm">
<li><pre>cd myapp-src-global</pre></li>
<li><pre>sh ./build_all.sh</pre></li>
</ul>
<p>
And you will find into the <code>myapp-src-all/dist/</code> folder all
the created installers.
</p>
</div>
<div class="howto-section">
<h4><span>5.5 Conclusions</span></h4>
<p>
Creating XULRunner applications is easy, powerful, and multiplatform,
like developing Firefox extensions.
</p>
<p>
You can use <a href="http://www.mozdev.org/projects/wizard/" rel="external">some wizards</a>
from mozdev.org to generate a skeleton to begin a new app (be careful,
you will have to adapt the
<a href="https://developer.mozilla.org/en/chrome_registration" rel="external">chrome registration</a>
for Firefox 4, the wizard need apparently to be updated for this part).
</p>
<p>
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.
</p>
<p>
Now it's up to you to make some nice apps, I hope this how-to and the
global script will help you.
<br />
Have fun ;) !
</p>
</div>
<div class="howto-navigator-bottom">
<div class="howto-previous"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribute-your-XULRunner-app-4-Mac-OSX" title="Go to chapter 4">< previous chapter</a></div>
<div class="clearer"> </div>
</div>
<div class="howto-author">
<p>Nicolas Martin</p>
</div>
<div class="howto-download-section">
<p>
You can download all the samples of this chapter 5, the global script
and XAL included, in the
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/dl.php?id=current_global" class="howto-download-archive">myapp-src-global.tar.gz</a>
archive.
</p>
</div>
<div class="howto-homepage-section">
<p>
Or retrieve this entire how-to in its
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/en/" class="howto-homepage-link">
dedicated page</a>,
downloadable with all the examples.
</p>
</div>
<div class="howto-credits">
<p><em>
The myapp application, from
<a href="https://developer.mozilla.org/en/getting_started_with_xulrunner" rel="external">developer.mozilla.org</a>
is in the <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a>.
</em></p>
<p><em>
The icon used is from the
<a href="http://tango.freedesktop.org" rel="external">Tango Desktop Project</a>,
and is in the <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a>.
</em></p>
<p><em>
The C launcher
<a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">XUL App Launcher</a> (XAL)
is under the <a href="http://www.opensource.org/licenses/mit-license.php" rel="external">MIT license</a>.
</em></p>
<p><em>
All other added data, and sample files, of this chapter 5, are under
<a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a>s too.
</em></p>
</div>
<div class="howto-footer">
<p>
The content of this article
is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>
</p>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width: 0;" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a>
</div>
</div>http://joliclic.free.fr/blog/index.php?post/2011/05/27/Distribute-your-XULRunner-app-5-Synthesis-all-platforms#comment-formhttp://joliclic.free.fr/blog/index.php?feed/atom/comments/85Distribuer votre appli XULRunner - 4 - Mac OSXurn:md5:fbb35182cb06ddbd49c388fd519f2c8f2011-05-26T17:00:00+02:00joliclicmozilladist_xul_app_frhowtoxul <div class="howto">
<p><em>This post exists also in <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribute-your-XULRunner-app-4-Mac-OSX">english</a></em></p>
<p><em>
Ce billet fait partie d'une <a href="http://joliclic.free.fr/blog/index.php?tag/dist_xul_app_fr">série</a> sur comment déployer une application XULRunner.
Voir le <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/20/Distribuer-votre-appli-XULRunner-1-Preambule">préambule</a> pour le contexte.
</em></p>
<h3><span>4 - Mac OSX</span></h3>
<ul>
<li>4.1 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howtofr_dxr_4.1">Icônes pour Mac OSX</a></li>
<li>4.2 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howtofr_dxr_4.2">Créer un lanceur</a></li>
<li>4.3 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howtofr_dxr_4.3">Quelques spécifités Mac</a></li>
<li>4.4 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howtofr_dxr_4.4">Créer un "Application Bundle"</a></li>
<li>4.5 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howtofr_dxr_4.5">Empaqueter un tar.gz et un dmg</a></li>
</ul>
<div class="howto-section">
<h4><span>4.1 Icônes pour Mac OSX</span></h4>
<p>
Nous avons besoin d'une icône
<strong><a href="http://en.wikipedia.org/wiki/Icns" rel="external">icns</a></strong>
Sur Mac les fenêtres n'ont pas d'icône, mais, plus tard, nous en utiliserons
une dans le Dock. Et elle sera utilisée pour le bundle que nous créerons.
</p>
<p>
Nous pouvons générer ce fichier <code>.icns</code> directement depuis
Linux. La restriction est que nous ne pouvons pas inclure des images avec
pluieurs profondeur de couleur, mais des dimensions multiples est possible.
<br />
Nous avons besoin du programme <code>png2icns</code> compris dans le
paquet <strong>icnsutils</strong>.
<br />
Sur Debian/Ubuntu:
</p>
<p><code class="code-xterm-root">apt_get install icnsutils</code></p>
<p>Nous obtenoons notre fichier icns à partir de plusieurs png avec :</p>
<p><kbd class="code-xterm">png2icns icon.icns icon128.png icon48.png icon32.png icon16.png</kbd></p>
<p><em>
Vous trouverez un script bash avec cette ligne de commande dans l'archive
d'exemple relative à ce chapitre 4
(dans <code>samples_chapter_4/data/icons/</code>).
</em></p>
</div>
<div class="howto-section">
<h4><span>4.2 Créer un lanceur</span></h4>
<p>
Comme pour Linux, nous utiliserons un <strong>script shell</strong>
pour notre <strong>lanceur</strong>.
Mais nous avons plusieurs problèmes à résoudre.
</p>
<p>
le premier n'est pas trop difficile. Le programme <code>readlink</code>
disponible sur Mac OSX n'est pas la version GNU, et nous ne pouvons pas
utiliser l'option <code>-f</code>. Nous résouderons nous même cette fonctionnalité.
</p>
<p>
Un problème plus sérieux, est que <em>nous ne pouvons pas déterminer où
Firefox est installé</em>. Donc, ici, nous imposerons une limitation,
<em>Firefox <strong>doit</strong> être installer à son emplacement par défaut</em>,
c'est à dire <code>/Applications/</code> .
</p>
<p><em>
Note : si vous une suggestions pour déterminer où est installer Firefox,
merci de me le faire savoir ;) .
</em></p>
<p>
Voici ce script, <code><strong>myapp-mac.sh</strong></code>, bien que ce ne soit
pas exactement celui que nous utiliserons plus tard :
</p>
<pre>#!/bin/sh<br /><br />set -e<br /><br /># see http://stackoverflow.com/questions/7665/how-to-resolve-symbolic-links-in-a-shell-script<br /># get the absolute path of the executable<br />SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && \<br /> pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")<br /><br /># resolve symlinks<br />while [ -h $SELF_PATH ]; do<br /> DIR=$(dirname -- "$SELF_PATH")<br /> SYM=$(readlink $SELF_PATH)<br /> SELF_PATH=$(cd $DIR && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")<br />done<br /><br />CUR_DIR=$(dirname "$SELF_PATH")<br /><br />if [ -x /Applications/Firefox.app/Contents/MacOS/firefox-bin ]; then<br /> /Applications/Firefox.app/Contents/MacOS/firefox-bin -app "$CUR_DIR/application.ini" $@<br />elif [ -x /Library/Frameworks/XUL.framework/xulrunner-bin ]; then<br /> /Library/Frameworks/XUL.framework/xulrunner-bin "$CUR_DIR/application.ini" $@<br />else<br /> echo "Error: unable to find Firefox or XULRunner!"<br />fi</pre>
<p>
Si nous essayons ce lanceur, nous pouvons voir 2 autres problèmes,
l'icône affichée dans le dock est celui de Firefox, et le menu principal,
quand nous fermons toutes les fenêtres de notre appli, est celui de Firefox.
<br />
il y a des solutions à ces problèmes, comme nous le verrons dans les prochaines
parties.
</p>
<p>
<em>Notez que si vous voulez vraiment utiliser un tel script, sans un bundle,
vous pouvez vouloir l'utiliser avec l'extension <code>.command</code>
plutôt que <code>.sh</code>, car alors le fichier peut être double-cliqué
dans le Finder de Mac, ce qui ouvrira un terminal, puis l'appli.</em>
</p>
</div>
<div class="howto-section">
<h4><span>4.3 Quelques spécifités Mac</span></h4>
<p>
Une particularité sur Mac, est le <strong>Menu principal</strong>
contrôler par le système d'exploitation, et correspondant à l'application
courante qui a le focus. Et quand toutes les fenêtres d'une même application
sont fermées, ce menu reste présent tant que l'on ne quitte pas l'application;
</p>
<p>
Ceci est bien pris en charge dans une application XULRunner, mais nous
devons ajouter un peu de code pour celà.
</p>
<p>Quelques docuementation sur MDN:</p>
<ul>
<li>
<a rel="external" href="https://developer.mozilla.org/en/XUL_School/Adding_menus_and_submenus#Menus_on_Mac_OS_X">Menus on Mac OS X</a>
</li>
<li>
<a rel="external" href="https://developer.mozilla.org/en/XULRunner/MacFAQ#UI_notes">MacFAQ: UI_notes</a>
</li>
</ul>
<p>
Si notre appli utilises un élément <code>menubar</code> principal, XULRunner/Firefox
l'utilisera (en fait le premier menubar) pour créer ce menu spécial. Si vous n'en
avez pas, vous devriez en créer un, masquer par défaut pour les autres plateformes,
il permettra la construction de ce menu.
</p>
<p>
les entrées dans le menu "pomme" sont construits d'après des éléments avec
des <code>id</code> réservés. Vous devriez créer au moins quelques entrées,
pour permettre de quitter l'appli par exemple.
</p>
<p>
Voici la liste des id disponibles correspondants à ces entrées
(<a rel="external" href="http://mxr.mozilla.org/mozilla1.9.2/source/widget/src/cocoa/nsMenuBarX.mm#564">sources</a>) :
</p>
<table>
<thead>
<tr>
<th>id de l'élément</th>
<th>item du menu correspondant</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>aboutName</code></td>
<td>À propos de cette application</td>
</tr>
<tr>
<td><code>menu_preferences</code></td>
<td>Préférences...</td>
</tr>
<tr>
<td><code>menu_mac_services</code></td>
<td>Services</td>
</tr>
<tr>
<td><code>menu_mac_hide_app</code></td>
<td>Masquer l'appli</td>
</tr>
<tr>
<td><code>menu_mac_hide_others</code></td>
<td>Masquer les autres</td>
</tr>
<tr>
<td><code>menu_mac_show_all</code></td>
<td>Tout afficher</td>
</tr>
<tr>
<td><code>menu_FileQuitItem</code></td>
<td>Quitter</td>
</tr>
</tbody>
</table>
<p>
et voici un exemple d'un menu xul minimal pour cet usage :
</p>
<pre> <commandset id="main-commands"><br /> <command id="cmd:quit" oncommand="myappQuitApplication();"/><br /> </commandset><br /> <br /> <keyset id="ui-keys"><br /> <key id="key:quitApp" key="Q" modifiers="accel" command="cmd:quit"/><br /> <key id="key:hideApp" key="H" modifiers="accel"/><br /> <key id="key:hideOthersApp" key="H" modifiers="accel,alt"/><br /> </keyset><br /> <br /> <menubar id="main-menubar" hidden="true"><br /> <menu id="mac-menu"><br /> <menupopup><br /> <menuitem id="menu_mac_hide_app" label="Hide My App" key="key:hideApp"/><br /> <menuitem id="menu_mac_hide_others" label="Hide Others" key="key:hideOthersApp"/><br /> <menuitem id="menu_mac_show_all" label="Show All"/><br /> <menuitem id="menu_FileQuitItem" label="Quit" key="key:quitApp" command="cmd:quit"/><br /> </menupopup><br /> </menu><br /> </menubar></pre>
<p>
Bien sur, comme pour les autres parties de l'appli, les chaînes utilisées
dans les labels et clés
<a rel="external" href="https://developer.mozilla.org/en/Localization"><strong>devraient être localisées</strong></a>.
Mais ce n'est pas le propos de ce tutoriel.
</p>
<p>
La fonction <code>myappQuitApplication</code> a été ajoutée dans le fichier
<code>main.js</code> de notre appli :
</p>
<pre>function myappQuitApplication() {<br /> const Cc = Components.classes;<br /> const Ci = Components.interfaces;<br /> <br /> let appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].<br /> getService(Ci.nsIAppStartup);<br /> appStartup.quit(Ci.nsIAppStartup.eAttemptQuit);<br /> <br /> return true;<br />}</pre>
<p>
Enfin, le dernier problème : quand toutes les fenêtres de notre
appli sont fermées, en fait l'appli ne quitte pas, et le menu principal
demeure. Plus, tel quel, c'est le menu de Firefox que nous voyons.
<br />
En fait, il y a une fenêtre speciale et cachée que XULRunner utilises pour ce menu.
Donc nous devons créer une telle fenêtre, et définir une préférence pour
la spécifier.
</p>
<p>Voilà la préférence ajoutée dans <code>defaults/preferences/pref.js</code> :</p>
<pre>pref("browser.hiddenWindowChromeURL", "chrome://myapp/content/hiddenWindow.xul");</pre>
<p>Et le contenu de <code>hiddenWindow.xul</code>, qui contient uniquement notre menubar :</p>
<pre><window id="hiddenWindow"<br /> xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><br /> <script type="application/javascript"><br /> <![CDATA[<br />function myappQuitApplication() {<br /> const Cc = Components.classes;<br /> const Ci = Components.interfaces;<br /> <br /> let appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].<br /> getService(Ci.nsIAppStartup);<br /> appStartup.quit(Ci.nsIAppStartup.eAttemptQuit);<br /> <br /> return true;<br />}<br /> ]]><br /> </script><br /> <br /> <commandset id="main-commands"><br /> <command id="cmd:quit" oncommand="myappQuitApplication();"/><br /> </commandset><br /> <br /> <keyset id="ui-keys"><br /> <key id="key:quitApp" key="Q" modifiers="accel" command="cmd:quit"/><br /> <key id="key:hideApp" key="H" modifiers="accel"/><br /> <key id="key:hideOthersApp" key="H" modifiers="accel,alt"/><br /> </keyset><br /> <br /> <menubar id="main-menubar" hidden="true"><br /> <menu id="mac-menu"><br /> <menupopup><br /> <menuitem id="menu_mac_hide_app" label="Hide My App" key="key:hideApp"/><br /> <menuitem id="menu_mac_hide_others" label="Hide Others" key="key:hideOthersApp"/><br /> <menuitem id="menu_mac_show_all" label="Show All"/><br /> <menuitem id="menu_FileQuitItem" label="Quit" key="key:quitApp" command="cmd:quit"/><br /> </menupopup><br /> </menu><br /> </menubar><br /></window></pre>
</div>
<div class="howto-section">
<h4><span>4.4 Créer un "Application Bundle"</span></h4>
<p>
Sur Mac OSX, les applications sont empaquetées dans un format spécial
nommé <strong>Application Bundle</strong>. C'est en fait un dossier, nommé
avec l'extension <code>.app</code>, et avec une structure particulière.
</p>
<p>Quelques documentations utiles :</p>
<ul>
<li><a rel="external" href="http://www.mactipsandtricks.com/articles/Wiley_HT_appBundles2.lasso">Inside Application Bundles</a></li>
<li><a rel="external" href="http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/000-Introduction/introduction.html">Runtime Configuration Guidelines: Introduction</a></li>
<li><a rel="external" href="https://developer.mozilla.org/en/XULRunner/Creating_custom_app_bundles_for_Mac_OS_X">Custom app bundles for Mac OS X</a></li>
</ul>
<p>Voici une proposition d'un dossier <code>.app</code> pour notre appli :</p>
<pre> |- MyApp.app/<br /> |- Contents/<br /> |- MacOS/<br /> |- chrome/<br /> |- defaults/<br /> |- application.ini<br /> |- chrome.manifest<br /> |- foxstub<br /> |- myapp-mac.sh<br /> |- Resources/<br /> |- myapp.icns<br /> |- Info.plist<br /> |- PkgInfo</pre>
<p>
le dossier <code><strong>MacOS</strong></code> contient en fait tous les fichiers
et dossiers de notre appli XULRunner.
</p>
<p>
le dossier <code><strong>Resources</strong></code> contient uniquement notre
icône au format icns.
</p>
<p>
Le fichier <code><strong>PkgInfo</strong></code> contient uniquement la chaîne
<code>APPL????</code>. Je ne suis pas sûr que ce fichier soit réellement nécessaire,
il semble important pour la compatibilité avec Mac OS 9.
<br />
La valeur spécifie que ce bundle est une application, et comme nous n'avons
pas d'identifiant de 4 caractères valide, nous utilisons 4 points d'interrogation
(<code>????</code>).
</p>
<p>
Le fichier <code><strong>Info.plist</strong></code> décrit notre appli :
</p>
<pre><?xml version="1.0" encoding="UTF-8"?><br /><!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><br /><plist version="1.0"><br /><dict><br /> <key>CFBundleDevelopmentRegion</key><br /> <string>English</string><br /> <key>CFBundleExecutable</key><br /> <string>myapp-mac.sh</string><br /> <key>CFBundleGetInfoString</key><br /> <string>MyApp 1.0</string><br /> <key>CFBundleIconFile</key><br /> <string>myapp</string><br /> <key>CFBundleIdentifier</key><br /> <string>net.yourcompany.myapp</string><br /> <key>CFBundleInfoDictionaryVersion</key><br /> <string>6.0</string><br /> <key>CFBundleName</key><br /> <string>MyApp</string><br /> <key>CFBundlePackageType</key><br /> <string>APPL</string><br /> <key>CFBundleShortVersionString</key><br /> <string>1.0.0.0</string><br /> <key>CFBundleSignature</key><br /> <string>????</string><br /> <key>CFBundleVersion</key><br /> <string>1.0.0.0</string><br /> <key>NSAppleScriptEnabled</key><br /> <true/><br /></dict><br /></plist></pre>
<p>Quelques commentaires sur ces propriétés :</p>
<p>
<code><strong>CFBundleExecutable</strong></code> pointe sur notre lanceur,
le script shell nommé <code>myapp-mac.sh</code>.
</p>
<p>
la valeur de <code><strong>CFBundleIconFile</strong></code> est
<code>myapp</code> parce que notre icône dans le dossier Resources est nommée
<code><strong>myapp</strong>.icns</code>. Maintenant, l'utilisateur final
visualise notre bundle avec notre icône.
</p>
<p>
<code><strong>CFBundleIdentifier</strong></code> est formaté tel que
<code>domaine inversé point nom de l'application</code>. Ce doit être un
identifiant unique pour notre appli.
</p>
<p>
<code><strong>CFBundlePackageType</strong></code> spécifie que c'est un
"application bundle" (<code>APPL</code>).
</p>
<p>
La valeur de <code><strong>CFBundleSignature</strong></code> est
<code>????</code> parce que nous n'avons pas de signature. C'est supposé
être un identifiant unique de 4 caractères, et supposé être obtenue
après un enregistrement auprès d'Apple. Tel que je le comprend, c'est
exiger essentiellement pour Mac OS 9. Ne vous inquiétez pas, je n'ai pas
vu de problème avec cette valeur ;) .
</p>
<p>
Maintenant voyons <strong>une belle astuce</strong>. Tel quel, l'utilisateur
final voit notre appli avec notre icône, mais quand il la lance, l'icône
dans le dock est celle de Firefox.
<br />
Dans l'arborescence du dossier de notre bundle, vous pouvez voir un fichier
nommé <code><strong>foxstub</strong></code>, ce fichier est en fait un
<strong>lien symbolique vers l'exécutable de Firefox</strong>.
</p>
<p>Ce lien est obtenu simplement, dans le dossier MacOS, avec :</p>
<p>
<kbd class="code-xterm">ln -s /Applications/Firefox.app/Contents/MacOS/firefox-bin foxstub</kbd>
</p>
<p>
Maintenant quand l'utilisateur lance notre appli, c'est bien notre icône
qui est affichée dans le dock :) .
</p>
<p><em>
En fait, notre icône apparait dans le dock, puis disparait, et réapparait
de nouveau, parce que notre script est lancé, puis l'exécutable lié. mais ce
n'est pas un gros problème.
</em></p>
<p>
Voici le script modifié de notre lanceur <code>myapp-mac.sh</code>, qui
utilise dorénavant le lien symbolique vers Firefox :
</p>
<pre>#!/bin/sh<br /><br />set -e<br /><br /># see http://stackoverflow.com/questions/7665/how-to-resolve-symbolic-links-in-a-shell-script<br /># get the absolute path of the executable<br />SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && \<br /> pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")<br /><br /># resolve symlinks<br />while [ -h $SELF_PATH ]; do<br /> DIR=$(dirname -- "$SELF_PATH")<br /> SYM=$(readlink $SELF_PATH)<br /> SELF_PATH=$(cd $DIR && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")<br />done<br /><br />CUR_DIR=$(dirname "$SELF_PATH")<br /><br />if [ -x "$CUR_DIR/foxstub" ]; then<br /> "$CUR_DIR/foxstub" -app "$CUR_DIR/application.ini" $@<br />else<br /> echo "Error: unable to find Firefox or XULRunner!"<br />fi</pre>
<p>
Nous allons générer notre "application bundle" à la'ide d'un script bash.
Il est vraiment simple, nous avons juste à copier le contenu de notre appli
dans le dossier MacOS. Voici le contenu de <code>build_mac.sh</code> :
</p>
<pre>#!/bin/bash<br /><br /># exit the script on errors<br />set -e<br /><br />TMP_DIR=./tmp<br /><br /># get the absolute path of our TMP_DIR folder<br />TMP_DIR=$(readlink -f -- "$TMP_DIR")<br /><br /># re-create the TMP_DIR folder<br />rm -rfv "$TMP_DIR"<br />mkdir -v "$TMP_DIR"<br /><br /># create the .app folder<br />mkdir -v "$TMP_DIR/MyApp.app"<br /><br /># copy our bundle skeleton<br /># (our launcher myapp-mac.sh and our symbolic link foxstub are included)<br />cp -rv data/bundle_skelet/. "$TMP_DIR/MyApp.app/"<br /><br /># copy our app<br />cp -rv myapp/* "$TMP_DIR/MyApp.app/Contents/MacOS/"<br /><br /># apply the correct permissions<br />chmod -R 755 "$TMP_DIR/MyApp.app"<br /><br /># and copy the result in our main src folder<br />rm -frv MyApp.app<br />cp -Rv "$TMP_DIR/MyApp.app" ./</pre>
<p>
Ce script sera continuer par la suite. Mais pour le moment, en résultat,
nous avons créer "l'Application Bundle" <code>MyApp.app</code>
dans notre dossier source principal.
</p>
</div>
<div class="howto-section">
<h4><span>4.5 Empaqueter un tar.gz et un dmg</span></h4>
<p>
Pour distribuer notre bundle, nous devons <strong>créer une archive</strong>.
Je propose 2 solutions, chacune avec ses avantages et inconvénients.
</p>
<h5>Créer un tar.gz</h5>
<p>
La première et <strong>la plus simple</strong>, est de <strong>créer un tar.gz</strong>.
Ce format est bien pris en charge par Mac OSX. Le seul problème est l'utilisateur
final risque de ne pas trouver celà très convivial. Voici ce qu'il a à faire :
</p>
<ol>
<li>télécharger le fichier myapp.tar.gz.</li>
<li>l'ouvrir. En fait le comportement par défaut est de décompresser l'archive dans le même dossier.</li>
<li>aller dans le dossier décompressé</li>
<li>déplacer par glisser/déposer le bundle où il le souhaite, par exemple dans /Applications/</li>
</ol>
<p>
Le problème est la phase 2, certains utilisateurs ne comprennent pas que
l'archive est décompressée, et où.
</p>
<p>
pour créer ce tar.gz, nous ajoutons les lignes suivantes dans notre
script <code>build_mac.sh</code> :
</p>
<pre># create a sub-directory for the tar.gz creation<br />mkdir "$TMP_DIR/myapp-1.0"<br /><br /># copy our bundle<br /># (It would be possible to add other files, the license for example)<br />cp -rv "$TMP_DIR/MyApp.app" "$TMP_DIR/myapp-1.0/"<br /><br />rm -fv myapp-mac-1.0.tar.gz<br /><br />cd "$TMP_DIR"<br />tar -zcvf "../myapp-mac-1.0.tar.gz" myapp-1.0<br />cd -</pre>
<h5>Créer un dmg</h5>
<p>
la seconde solution est de <strong>créer une
<a rel="external" href="http://en.wikipedia.org/wiki/Apple_Disk_Image">archive dmg</a></strong>.
En fait c'est un format d'image disque.
<br />
C'est une solution habituelle sur cette plateforme, et utilisée par Firefox par exemple.
</p>
<p>
Nous pouvons <strong>créer un dmg depuis Linux</strong>, mais avec une limitation :
nous ne pouvons pas générer de dmg <strong>compressé</strong>. Enfin en fait il y a une
méthode expérimentale de le faire, que je vais vous proposé ensuite, mais elle est encore
expérimentale.
<br />
<em>L'article le plus utile que j'ai trouvé sur ce sujet est
<a rel="external" href="http://blog.dmdirc.com/2008/06/13/os-x-support-part-2/">ce billet</a>
de l'auteur de DMDirc. Nous allons suivre les mêmes méthodes.</em>
</p>
<p>
La méthode la plus simple sur linux est d'utiliser <code>mkisofs</code>,
ou <code>genisoimage</code>.
<code>genisoimage</code> est un fork du premier, utilisé par certaines distributions,
et comme un lien symbolique nommé <code>mkisofs</code> est également installé par ces
distributions, j'utiliserai la commande <code>mkisofs</code> dans la suite du script.
<br />
Pour l'installer sur Debian/Ubuntu :
</p>
<p><code class="code-xterm-root">apt-get install genisoimage</code></p>
<p>
Nous ajoutons dans notre script <code>build_mac.sh</code> :
</p>
<pre># create the dmg<br />rm -fv myapp-1.0.dmg<br />mkisofs -V 'MyApp' -no-pad -r -apple -o "myapp-1.0.dmg" "$TMP_DIR/myapp-1.0"</pre>
<p>
Avec cette ligne, nous créons le fichier <code>myapp-1.0.dmg</code>, qui contient
tous les fichiers et dossiers inclus dans le dossier <code>$TMP_DIR/myapp-1.0</code>,
avec un nom de volume <code>'MyApp'</code> (le titre de la fenêtre quand le dmg
est monté sur Mac). Et tous les fichiers et dossiers dans le dmg ont les droits 755.
the 755 rights.
</p>
<h5>Compresser ce dmg</h5>
<p>
La seule solution trouvée pour réaliser cette tâche sur Linux, mentionné
dans ce
<a rel="external" href="http://blog.dmdirc.com/2008/06/13/os-x-support-part-2/">billet</a>,
est d'utiliser le programme <code>dmg</code> du projet
<a rel="external" href="http://github.com/planetbeing/libdmg-hfsplus">libdmg-hfsplus</a>.
<br />
L'auteur dit clairement :
</p>
<blockquote>
<p>THE CODE HEREIN SHOULD BE CONSIDERED HIGHLY EXPERIMENTAL</p>
</blockquote>
<p><em>traduction approximative :</em></p>
<blockquote>
<p>CE CODE DOIT ÊTRE CONSIDÉRER COMME HAUTEMENT EXPÉRIMENTAL</p>
</blockquote>
<p>
Noter que le dernier commit du projet que j'ai essayé ne compilait pas
(quelqu'un a rapporté
<a rel="external" href="http://github.com/planetbeing/libdmg-hfsplus/issues#issue/2">le bug</a>).
<br />
Et l'auteur de DMDirc rapporte un autre bug d'une version précédente.
Mais essayer sa
And the author of DMDirc report another bug in a previous version. But I have
tried its <a rel="external" href="http://shanemcc.co.uk/libdmg/">version légèrement modifiée</a>, avec succès !
<br />
Donc vous devriez l'essayer.
</p>
<p>
Voici les lignes ajoutées à notre script :
</p>
<pre># try to compress our dmg, if the the command <dmg> is available<br /># see http://github.com/planetbeing/libdmg-hfsplus<br /># and http://shanemcc.co.uk/libdmg/<br />if [ -x "./tools/dmg" ]; then<br /> # rename temporarily our dmg<br /> mv myapp-1.0.dmg myapp-1.0.dmg.pre<br /> # compress<br /> "./tools/dmg" dmg myapp-1.0.dmg.pre myapp-1.0.dmg<br /> rm -fv myapp-1.0.dmg.pre<br />fi</pre>
<p><em>
Noter que je n'inclus pas le programme <code>dmg</code> dans l'archive jointe,
vous devrez le télécharger vous même depuis les liens précédents, et le
placer dans le dossier <code>tools</code>.
</em></p>
<h5>Créer un dmg depuis Mac OSX</h5>
<p>
pour information, pour créer un dmg directement depuis Mac OSX, il existe le
programme
<a rel="external" href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/hdiutil.1.html">hdiutil</a>.
Pour créer un dmg compressé et s'ouvrant automatiquement, d'après un
dossier nommé "myFolder", quelque chose du genre devrait marcher :
</p>
<pre>#!/bin/bash<br /><br />hdiutil create -volname "myapp-1.0" -fs HFS+ -srcfolder "myFolder" -format UDRW myapp-temp.dmg <br /><br />hdiutil convert myapp-temp.dmg -format UDZO -imagekey zlib-level=9 -o myapp.dmg<br /><br />rm -fv myapp-temp.dmg</pre>
<h5>Personnaliser le dmg</h5>
<p>
il est possible de personnaliser notre dmg quand il est monté, c'est à dire
la fenêtre affichée, par exemple : dimensions de l'icône, image d'arrière plan,
dimensions de la fenêtre...
</p>
<p>
L'idée est de créer un dmg en lecture/écriture sur un mac, de le personnaliser
manuellement, puis de faire une copie du fichier
<a rel="external" href="http://en.wikipedia.org/wiki/.DS_Store">.DS_STORE</a>
et de toutes les autres données. Ensuite nous utiliserons ces données lors
de la création de notre dmg à l'aide de notre script.
<br />
Cette partie ne peut être faite que manuellement et directement depuis un Mac.
</p>
<h6>Créer une image disque en lecture/écriture</h6>
<p>
Sur un Mac, ouvrez "Utilitaires de disque" dans le dossier "Utilitaires".
<br />
Cliquer sur "Nouvelle image".
<br />
Choisissez le <strong>même nom</strong> que le futur nom de volume de notre dmg
(<code>MyApp</code>).
<br />
N'utilisez pas un format journalisé, pour être capable de choisir une
"petite" taille pour ce dmg. La taille importe peu, elle doit juste être
suffisante pour copier notre appli et les autres données.
<br />
Comme <code>format</code> d'image, choisissez <code>image disque en lecture et écriture</code>
Puis cliquer sur "créer".
</p>
<h6>Ajouter nos données sur l'image disque</h6>
<p>
Maintenant montez ce dmg en cliquant dessus. Et ouvrez le.
<br />
Copiez notre appli dans la fenêtre ouverte.
Nous pouvons copier le lien symbolique vers <code>/Applications</code> également,
si il existe. Sinon nous le créerons dans un terminal plus tard.
</p>
<p>
Ouvrez un terminal, depuis les Utilitaires.
<br />
Allez dans l'image disque monté :
<br />
<kbd class="code-xterm">cd /Volumes/MyApp</kbd>
<br />
puis créez un dossier nommé <code>.background</code> :
<br />
<kbd class="code-xterm">mkdir .background</kbd>
<br />
comme ce nom commence par un point (<code>.</code>), ce dossier est caché,
comme sur Linux.
<br />
copiez notre image d'arrière plan dans ce dossier :
<br />
<kbd class="code-xterm">cp pathToOurImage/background.png /Volumes/MyApp/.background/</kbd>
<br />
Et, si besoin, créez le lien symbolique vers /Applications :
<br />
<kbd class="code-xterm">ln -s /Applications /Volumes/MyApp/Applications</kbd>
</p>
<h6>Personnaliser l'image disque</h6>
<p>
Maintenant donnez le focus à la fenêtre du disque monté, et ouvrez
"Afficher les options de présentation" depuis le menu "Présentation"
<br />
Sélectionner <code>Image</code> pour l'airrière plan, puis cliquer sur le bouton.
<br />
Nous devons sélectionner le fichier background.png dans le disque monté,
mais comme ce fichier est dans un dossier caché, nous ne pouvons pas le voir.
Pas d'inquiétude, taper <code>Apple+Shift+G</code> :
<br />
puis taper <code>/Volumes/MyApp/.background/</code>, puis entrée.
<br />
sélectionnez maintenant notre png.
<br />
Choissisez la taille d'icône, par exemple 128, arrangez les par <code>aucun</code>,
puis déplacez les où vous le souhaitez, et adapter la taille de fenêtre
à votre arrière-plan.
</p>
<p>
Maintenant fermer cette fenêtre, et éjecter le disque. Puis monter le de
nouveau, et ouvrez le.
</p>
<h6>Copier la personnalisation créée</h6>
<p>
Dans un terminal, copiez le fichier <code>.DS_STORE</code> créé à la racine
du disque monté :
<br />
<kbd class="code-xterm">cp /Volumes/MyApp/.DS_STORE pathToSave/myDS_STORE</kbd>
</p>
<p>
Maintenant nous avons enfin tous les fichiers nécessaires pour notre script,
nous avons juste à copier le fichier .DS_STORE et le dossier .background avec
le fichier background.png . Le dmg que nous créerons aura la même personnalisation.
<br />
Copions ces données dans nos sources :
</p>
<pre> |- samples_chapter_4/<br /> |- data/<br /> |- bundle_skelet/<br /> |- dmg_extra_data/<br /> |- .background/<br /> |- background.png<br /> |- Applications<br /> |- .DS_STORE</pre>
<p>
Et nous ajoutons les lignes suivantes à notre script <code>build_mac.sh</code>,
avant de créer le dmg :
</p>
<pre># copy eventual extra data, by example a symbolic link to /Applications<br />if [ $(ls -A "data/dmg_extra_data" | wc -c) -ne 0 ]; then<br /> cp -Rv data/dmg_extra_data/. "$TMP_DIR/myapp-1.0/"<br />fi</pre>
<p>
<em>
Notez que cette <strong>personnalisation est dépendente du nom de volume utilisé</strong>,
en particulier pour l'image d'arrière plan, parce que le fichier <code>.DS_STORE</code>
utilise des chemins absolus vers le disque monté, et le nom de volume en fait partie.
<br />
Donc si vous changer le nom de volume, vous devez recréer manuellement cette
personnalisation...
</em>
</p>
<h5>Lancer notre script de build</h5>
<p>Pour lancer notre script (sur Linux), et créer nos dmg et tar.gz, dans un terminal :</p>
<ul class="code-xterm">
<li><pre>cd samples_chapter_4</pre></li>
<li><pre>sh ./build_mac.sh</pre></li>
</ul>
<p>
Et, en résultat, nous avons finalement le dossier <code>MyApp.app</code>,
et les fichiers <code>myapp-1.0.dmg</code> et <code>myapp-1.0.tar.gz</code>
dans notre dossier de source principal.
</p>
</div>
<div class="howto-navigator-bottom">
<div class="howto-previous"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/Distribuer-votre-appli-XULRunner-3-Windows" title="Aller au chapitre 3">< chapitre précédent</a></div>
<div class="howto-next"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/Distribuer-votre-appli-XULRunner-5-Synthese-toutes-plateformes" title="Aller au chapitre 5">chapitre suivant ></a></div>
<div class="clearer"> </div>
</div>
<div class="howto-author">
<p>Nicolas Martin</p>
</div>
<div class="howto-download-section">
<p>
Vous pouvez télécharger tous les exemples de ce chapitre 4 (Mac OSX) dans l'archive
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/dl.php?id=current_sample_4" class="howto-download-archive">
samples_chapter_4.tar.gz
</a>.
</p>
</div>
<div class="howto-credits">
<p><em>
L'application myapp, de
<a href="https://developer.mozilla.org/en/getting_started_with_xulrunner" rel="external">developer.mozilla.org</a>,
est dans le <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domaine Public</a>.
</em></p>
<p><em>
L'icône utilisée est issue du
<a href="http://tango.freedesktop.org" rel="external">Tango Desktop Project</a>,
et est dans le <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domaine Public</a>.
</em></p>
<p><em>
Toutes les autres données ajoutées, et les fichiers en exemple, de ce chapitre 4, sont dans le
<a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domain Public</a> également.
</em></p>
</div>
<div class="howto-footer">
<p>
Le contenu de cet article est sous les termes de la licence
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>
</p>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width: 0;" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a>
</div>
</div>http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribuer-votre-appli-XULRunner-4-Mac-OSX#comment-formhttp://joliclic.free.fr/blog/index.php?feed/atom/comments/84Distribute your XULRunner app - 4 - Mac OSXurn:md5:e89ff800eefcdcbed90224326de10e662011-05-26T16:54:00+02:00joliclicmozilladist_xul_app_enhowtoxul <div class="howto">
<p><em>Ce billet existe aussi en <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribuer-votre-appli-XULRunner-4-Mac-OSX">français</a></em></p>
<p><em>
This post is part of a <a href="http://joliclic.free.fr/blog/index.php?tag/dist_xul_app_en">series</a> about how to package a XULRunner application.<br />
See the <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/20/Distribute-your-XULRunner-app-1-Preamble">preamble</a> for the context case.
</em></p>
<h3><span>4 - Mac OSX</span></h3>
<ul>
<li>4.1 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howto_dxr_4.1">Icons for Mac OSX</a></li>
<li>4.2 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howto_dxr_4.2">Create a launcher</a></li>
<li>4.3 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howto_dxr_4.3">Some Mac specificities</a></li>
<li>4.4 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howto_dxr_4.4">Create a Application Bundle</a></li>
<li>4.5 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/#howto_dxr_4.5">Package as tar.gz and dmg</a></li>
</ul>
<div class="howto-section">
<h4><span>4.1 Icons for Mac OSX</span></h4>
<p>
We need a <strong><a href="http://en.wikipedia.org/wiki/Icns" rel="external">icns</a></strong>
icon. On Mac the windows doesn't have a icon, but, later,
we will use one in the Dock. And it will be used for the bundle that we will
create.
</p>
<p>
We can generate this <code>.icns</code> file directly from Linux. The
restriction is that we cannot embed mutiple color depth images, but
multiple sizes is possible.
<br />
We need the program <code>png2icns</code> from the <strong>icnsutils</strong> package.
<br />
On Debian/Ubuntu:
</p>
<p><code class="code-xterm-root">apt_get install icnsutils</code></p>
<p>We obtain our icns file from several png with:</p>
<p><kbd class="code-xterm">png2icns icon.icns icon128.png icon48.png icon32.png icon16.png</kbd></p>
<p><em>
You can find a bash script with this command line in the sample archive
related to this chapter 4 (in <code>samples_chapter_4/data/icons/</code>).
</em></p>
</div>
<div class="howto-section">
<h4><span>4.2 Create a launcher</span></h4>
<p>
Like for Linux, we will use a <strong>shell script</strong> for our
<strong>launcher</strong>. But we have several problems to resolve.
</p>
<p>
The first one is not too difficult. The <code>readlink</code> program
available on Mac OSX is not the GNU one, and we can't use the
<code>-f</code> option. We will resolve ourself this functionality.
</p>
<p>
A more serious problem, is that <em>we can't determine where Firefox is
installed</em>. So, here, we will impose a limitation,
<em>Firefox <strong>must</strong> be installed in its default location</em>,
i.e. <code>/Applications/</code> .
</p>
<p><em>
Note: if you have any suggestions to determine where is installed Firefox,
please let me know ;) .
</em></p>
<p>
Here this script, <code><strong>myapp-mac.sh</strong></code>, but that's
not exactly this one we will use later:
</p>
<pre>#!/bin/sh<br /><br />set -e<br /><br /># see http://stackoverflow.com/questions/7665/how-to-resolve-symbolic-links-in-a-shell-script<br /># get the absolute path of the executable<br />SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && \<br /> pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")<br /><br /># resolve symlinks<br />while [ -h $SELF_PATH ]; do<br /> DIR=$(dirname -- "$SELF_PATH")<br /> SYM=$(readlink $SELF_PATH)<br /> SELF_PATH=$(cd $DIR && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")<br />done<br /><br />CUR_DIR=$(dirname "$SELF_PATH")<br /><br />if [ -x /Applications/Firefox.app/Contents/MacOS/firefox-bin ]; then<br /> /Applications/Firefox.app/Contents/MacOS/firefox-bin -app "$CUR_DIR/application.ini" $@<br />elif [ -x /Library/Frameworks/XUL.framework/xulrunner-bin ]; then<br /> /Library/Frameworks/XUL.framework/xulrunner-bin "$CUR_DIR/application.ini" $@<br />else<br /> echo "Error: unable to find Firefox or XULRunner!"<br />fi</pre>
<p>
If we try this launcher, we can see 2 other problems, the icon shown in the dock
is the Firefox one, and the main menu, when we close all our app windows, is the Firefox one.
<br />
There is a solution to these problems, as we will see in the next parts.
</p>
<p>
<em>Note that if you really want to use a such script, without a bundle,
you may want use a <code>.command</code> extension rather than <code>.sh</code>,
because then the file can be double-clicked in the Mac Finder, this will
open a terminal then the app.</em>
</p>
</div>
<div class="howto-section">
<h4><span>4.3 Some Mac specificities</span></h4>
<p>
There is a particularity on Mac, there is a <strong>main Menu</strong>
controlled by the operating system, corresponding to the current focused
application. And when all windows of a same application are closed, this
menu still appeared, as long as the application quit.
</p>
<p>
This is well handled by a XULRunner application, but we must add some
code for that.
</p>
<p>Some documentation on MDN:</p>
<ul>
<li>
<a rel="external" href="https://developer.mozilla.org/en/XUL_School/Adding_menus_and_submenus#Menus_on_Mac_OS_X">Menus on Mac OS X</a>
</li>
<li>
<a rel="external" href="https://developer.mozilla.org/en/XULRunner/MacFAQ#UI_notes">MacFAQ: UI_notes</a>
</li>
</ul>
<p>
If your app uses a main <code>menubar</code> element, XULRunner/Firefox
will use it (in fact the first menubar) to create this special menu. If
you don't have one, you should create it, hidden by default for the other
platforms, this will allow the menu construction anyway.
</p>
<p>
The entries in the apple menu are constructed from elements with
reserved <code>id</code>. You should at least create some entries, to
allow to quit the app for example.
</p>
<p>
Here's the list of available id corresponding to these entries
(<a rel="external" href="http://mxr.mozilla.org/mozilla1.9.2/source/widget/src/cocoa/nsMenuBarX.mm#564">sources</a>):
</p>
<table>
<thead>
<tr>
<th>element id</th>
<th>corresponding menu item</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>aboutName</code></td>
<td>About This App</td>
</tr>
<tr>
<td><code>menu_preferences</code></td>
<td>Preferences...</td>
</tr>
<tr>
<td><code>menu_mac_services</code></td>
<td>Services</td>
</tr>
<tr>
<td><code>menu_mac_hide_app</code></td>
<td>Hide App</td>
</tr>
<tr>
<td><code>menu_mac_hide_others</code></td>
<td>Hide Others</td>
</tr>
<tr>
<td><code>menu_mac_show_all</code></td>
<td>Show All</td>
</tr>
<tr>
<td><code>menu_FileQuitItem</code></td>
<td>Quit</td>
</tr>
</tbody>
</table>
<p>
And here's an example of a minimal xul menu for this usage:
</p>
<pre> <commandset id="main-commands"><br /> <command id="cmd:quit" oncommand="myappQuitApplication();"/><br /> </commandset><br /> <br /> <keyset id="ui-keys"><br /> <key id="key:quitApp" key="Q" modifiers="accel" command="cmd:quit"/><br /> <key id="key:hideApp" key="H" modifiers="accel"/><br /> <key id="key:hideOthersApp" key="H" modifiers="accel,alt"/><br /> </keyset><br /> <br /> <menubar id="main-menubar" hidden="true"><br /> <menu id="mac-menu"><br /> <menupopup><br /> <menuitem id="menu_mac_hide_app" label="Hide My App" key="key:hideApp"/><br /> <menuitem id="menu_mac_hide_others" label="Hide Others" key="key:hideOthersApp"/><br /> <menuitem id="menu_mac_show_all" label="Show All"/><br /> <menuitem id="menu_FileQuitItem" label="Quit" key="key:quitApp" command="cmd:quit"/><br /> </menupopup><br /> </menu><br /> </menubar></pre>
<p>
Of course, like the other part of the app, the strings used in the labels and keys
<a rel="external" href="https://developer.mozilla.org/en/Localization"><strong>should be localized</strong></a>.
But that's not the purpose of this howto.
</p>
<p>
The <code>myappQuitApplication</code> function has been added in the
<code>main.js</code> file of the our app:
</p>
<pre>function myappQuitApplication() {<br /> const Cc = Components.classes;<br /> const Ci = Components.interfaces;<br /> <br /> let appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].<br /> getService(Ci.nsIAppStartup);<br /> appStartup.quit(Ci.nsIAppStartup.eAttemptQuit);<br /> <br /> return true;<br />}</pre>
<p>
Finally, the last problem: when all windows of our app are closed,
in fact the app doesn't quit, and the main menu remains. More, as is,
this is the Firefox menu that we see.
<br />
In fact, there is a special hidden window that XULRunner uses for this menu.
So we have to create a such window, and to set a preference to specify it.
</p>
<p>Here's the added preference in <code>defaults/preferences/pref.js</code>:</p>
<pre>pref("browser.hiddenWindowChromeURL", "chrome://myapp/content/hiddenWindow.xul");</pre>
<p>And the content of <code>hiddenWindow.xul</code>, which contains only our main menubar:</p>
<pre><window id="hiddenWindow"<br /> xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><br /> <script type="application/javascript"><br /> <![CDATA[<br />function myappQuitApplication() {<br /> const Cc = Components.classes;<br /> const Ci = Components.interfaces;<br /> <br /> let appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].<br /> getService(Ci.nsIAppStartup);<br /> appStartup.quit(Ci.nsIAppStartup.eAttemptQuit);<br /> <br /> return true;<br />}<br /> ]]><br /> </script><br /> <br /> <commandset id="main-commands"><br /> <command id="cmd:quit" oncommand="myappQuitApplication();"/><br /> </commandset><br /> <br /> <keyset id="ui-keys"><br /> <key id="key:quitApp" key="Q" modifiers="accel" command="cmd:quit"/><br /> <key id="key:hideApp" key="H" modifiers="accel"/><br /> <key id="key:hideOthersApp" key="H" modifiers="accel,alt"/><br /> </keyset><br /> <br /> <menubar id="main-menubar" hidden="true"><br /> <menu id="mac-menu"><br /> <menupopup><br /> <menuitem id="menu_mac_hide_app" label="Hide My App" key="key:hideApp"/><br /> <menuitem id="menu_mac_hide_others" label="Hide Others" key="key:hideOthersApp"/><br /> <menuitem id="menu_mac_show_all" label="Show All"/><br /> <menuitem id="menu_FileQuitItem" label="Quit" key="key:quitApp" command="cmd:quit"/><br /> </menupopup><br /> </menu><br /> </menubar><br /></window></pre>
</div>
<div class="howto-section">
<h4><span>4.4 Create a Application Bundle</span></h4>
<p>
On Mac OSX, applications are packaged in a special format named
<strong>Application Bundle</strong>. This is in fact a folder, named with
<code>.app</code> as extension, and with a special structure.
</p>
<p>Some useful documentation:</p>
<ul>
<li><a rel="external" href="http://www.mactipsandtricks.com/articles/Wiley_HT_appBundles2.lasso">Inside Application Bundles</a></li>
<li><a rel="external" href="http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/000-Introduction/introduction.html">Runtime Configuration Guidelines: Introduction</a></li>
<li><a rel="external" href="https://developer.mozilla.org/en/XULRunner/Creating_custom_app_bundles_for_Mac_OS_X">Custom app bundles for Mac OS X</a></li>
</ul>
<p>Here's a proposal <code>.app</code> folder for our app:</p>
<pre> |- MyApp.app/<br /> |- Contents/<br /> |- MacOS/<br /> |- chrome/<br /> |- defaults/<br /> |- application.ini<br /> |- chrome.manifest<br /> |- foxstub<br /> |- myapp-mac.sh<br /> |- Resources/<br /> |- myapp.icns<br /> |- Info.plist<br /> |- PkgInfo</pre>
<p>
The <code><strong>MacOS</strong></code> folder contains in fact all the
files and folders of our XULRunner app.
</p>
<p>
The <code><strong>Resources</strong></code> folder contains only our icon
in icns format.
</p>
<p>
The <code><strong>PkgInfo</strong></code> file contains only the string
<code>APPL????</code>. I'm not sure this file is really necessary, it's
seems it's important for compatibilty with Mac OS 9.
<br />
The value specifies that this bundle is an application, and as we don't have
a valid 4 characters identifier, we use 4 question marks (<code>????</code>).
</p>
<p>
The <code><strong>Info.plist</strong></code> file describe our app:
</p>
<pre><?xml version="1.0" encoding="UTF-8"?><br /><!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><br /><plist version="1.0"><br /><dict><br /> <key>CFBundleDevelopmentRegion</key><br /> <string>English</string><br /> <key>CFBundleExecutable</key><br /> <string>myapp-mac.sh</string><br /> <key>CFBundleGetInfoString</key><br /> <string>MyApp 1.0</string><br /> <key>CFBundleIconFile</key><br /> <string>myapp</string><br /> <key>CFBundleIdentifier</key><br /> <string>net.yourcompany.myapp</string><br /> <key>CFBundleInfoDictionaryVersion</key><br /> <string>6.0</string><br /> <key>CFBundleName</key><br /> <string>MyApp</string><br /> <key>CFBundlePackageType</key><br /> <string>APPL</string><br /> <key>CFBundleShortVersionString</key><br /> <string>1.0.0.0</string><br /> <key>CFBundleSignature</key><br /> <string>????</string><br /> <key>CFBundleVersion</key><br /> <string>1.0.0.0</string><br /> <key>NSAppleScriptEnabled</key><br /> <true/><br /></dict><br /></plist></pre>
<p>Some comment on these properties:</p>
<p>
The <code><strong>CFBundleExecutable</strong></code> point to our launcher,
the shell script named <code>myapp-mac.sh</code>.
</p>
<p>
The value of <code><strong>CFBundleIconFile</strong></code> is
<code>myapp</code> because our icon in the Resources folder is named
<code><strong>myapp</strong>.icns</code>. Now, the end user sees our bundle
with our icon.
</p>
<p>
The <code><strong>CFBundleIdentifier</strong></code> is formatted as
an <code>inversed domain name dot application name</code>. It must be a unique
identifier for our app.
</p>
<p>
The <code><strong>CFBundlePackageType</strong></code> specifies that this is an
application bundle (<code>APPL</code>).
</p>
<p>
The value of <code><strong>CFBundleSignature</strong></code> is
<code>????</code> because we don't have a signature. This is supposed to
be an unique 4 characters identifier, and supposed to be obtained by an
Apple registration. As I understand this is required essentially for
Mac OS 9. Don't worry, I have not seen any problem with our value ;) .
</p>
<p>
Now let's see <strong>a nice trick</strong>. As is, the end user sees our
app with our icon, but when he launches it, the icon in the Dock is the
Firefox one.
<br />
In the tree folder of our bundle, you can see a file named
<code><strong>foxstub</strong></code>, this file is in fact
<strong>a symbolic link to the Firefox executable</strong>.
</p>
<p>This link is obtained simply, in the MacOS folder, with:</p>
<p>
<kbd class="code-xterm">ln -s /Applications/Firefox.app/Contents/MacOS/firefox-bin foxstub</kbd>
</p>
<p>
Now when the user launches our app, this is our icon that's displayed in
the dock :) .
</p>
<p><em>
In fact, our icon appears in the dock, then disappears, and appears again,
because our script is launched, then the linked executable. But that's
not a big problem.
</em></p>
<p>
Here's the modified launcher script <code>myapp-mac.sh</code>, which now
uses the symbolic link to firefox:
</p>
<pre>#!/bin/sh<br /><br />set -e<br /><br /># see http://stackoverflow.com/questions/7665/how-to-resolve-symbolic-links-in-a-shell-script<br /># get the absolute path of the executable<br />SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && \<br /> pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")<br /><br /># resolve symlinks<br />while [ -h $SELF_PATH ]; do<br /> DIR=$(dirname -- "$SELF_PATH")<br /> SYM=$(readlink $SELF_PATH)<br /> SELF_PATH=$(cd $DIR && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")<br />done<br /><br />CUR_DIR=$(dirname "$SELF_PATH")<br /><br />if [ -x "$CUR_DIR/foxstub" ]; then<br /> "$CUR_DIR/foxstub" -app "$CUR_DIR/application.ini" $@<br />else<br /> echo "Error: unable to find Firefox or XULRunner!"<br />fi</pre>
<p>
We will generate our application bundle with a bash script.
It's very simple, we have just to copy the content of our app into the MacOS
folder. Here's the content of <code>build_mac.sh</code>:
</p>
<pre>#!/bin/bash<br /><br /># exit the script on errors<br />set -e<br /><br />TMP_DIR=./tmp<br /><br /># get the absolute path of our TMP_DIR folder<br />TMP_DIR=$(readlink -f -- "$TMP_DIR")<br /><br /># re-create the TMP_DIR folder<br />rm -rfv "$TMP_DIR"<br />mkdir -v "$TMP_DIR"<br /><br /># create the .app folder<br />mkdir -v "$TMP_DIR/MyApp.app"<br /><br /># copy our bundle skeleton<br /># (our launcher myapp-mac.sh and our symbolic link foxstub are included)<br />cp -rv data/bundle_skelet/. "$TMP_DIR/MyApp.app/"<br /><br /># copy our app<br />cp -rv myapp/* "$TMP_DIR/MyApp.app/Contents/MacOS/"<br /><br /># apply the correct permissions<br />chmod -R 755 "$TMP_DIR/MyApp.app"<br /><br /># and copy the result in our main src folder<br />rm -frv MyApp.app<br />cp -Rv "$TMP_DIR/MyApp.app" ./</pre>
<p>
This script will be continued in the next parts. But for the moment,
as result, we have created the Application Bundle <code>MyApp.app</code>
into our main source folder.
</p>
</div>
<div class="howto-section">
<h4><span>4.5 Package as tar.gz and dmg</span></h4>
<p>
To distribute our bundle, we need to <strong>create an archive</strong>.
I propose here 2 solutions, each with advantages and disadvantages.
</p>
<h5>Create a tar.gz</h5>
<p>
The first and the <strong>simplest</strong>, is to <strong>create a tar.gz</strong>.
This format is well handled by Mac OSX. The only problem, is that the
final user can find it not really friendly. Here what's he have to do:
</p>
<ol>
<li>download the myapp.tar.gz file.</li>
<li>open it. In fact the default behavior is to uncompress the archive into the same folder.</li>
<li>go into the uncompressed folder</li>
<li>move by drag and drop the bundle where he wants, for example in /Applications/</li>
</ol>
<p>
The problem is phase 2, some users doesn't understand that the archive
is uncompressed, and where.
</p>
<p>To create this tar.gz, we add the following lines in our <code>build_mac.sh</code> script:</p>
<pre># create a sub-directory for the tar.gz creation<br />mkdir "$TMP_DIR/myapp-1.0"<br /><br /># copy our bundle<br /># (It would be possible to add other files, the license for example)<br />cp -rv "$TMP_DIR/MyApp.app" "$TMP_DIR/myapp-1.0/"<br /><br />rm -fv myapp-mac-1.0.tar.gz<br /><br />cd "$TMP_DIR"<br />tar -zcvf "../myapp-mac-1.0.tar.gz" myapp-1.0<br />cd -</pre>
<h5>Create a dmg</h5>
<p>
The second solution is to <strong>create a
<a rel="external" href="http://en.wikipedia.org/wiki/Apple_Disk_Image">dmg archive</a></strong>.
In fact this is a disk image format.
<br />
This is a usual solution on this platform, and used by Firefox for example.
</p>
<p>
We can <strong>create a dmg from Linux</strong>, but with a limitation:
we can't generate a <strong>compressed</strong> dmg.
Well, in fact there's a experimental way to do it, that I will propose to
you just after, but it's still experimental.
<br />
<em>The most useful article that I have found about this subject is
<a rel="external" href="http://blog.dmdirc.com/2008/06/13/os-x-support-part-2/">this post</a>
from the author of DMDirc. We will follow the same methods.</em>
</p>
<p>
The easiest way on linux is to use <code>mkisofs</code>, or <code>genisoimage</code>.
<code>genisoimage</code> is a fork of the former, used in some distributions, and as
a symlink named <code>mkisofs</code> is installed as well on these distributions, I will
use the command <code>mkisofs</code> in the following script.
<br />
To install it on Debian/Ubuntu:
</p>
<p><code class="code-xterm-root">apt-get install genisoimage</code></p>
<p>
We add, in our build script <code>build_mac.sh</code>:
</p>
<pre># create the dmg<br />rm -fv myapp-1.0.dmg<br />mkisofs -V 'MyApp' -no-pad -r -apple -o "myapp-1.0.dmg" "$TMP_DIR/myapp-1.0"</pre>
<p>
With this line, we create the file <code>myapp-1.0.dmg</code>, which contains
all files and folders included in the <code>$TMP_DIR/myapp-1.0</code> folder,
with the volume name <code>'MyApp'</code> (the title of the window when the dmg
is mounted on Mac). And all files and folders in the dmg have
the 755 rights.
</p>
<h5>Compress this dmg</h5>
<p>
The only solution found to perform this task on Linux, mentioned in this
<a rel="external" href="http://blog.dmdirc.com/2008/06/13/os-x-support-part-2/">post</a>,
is to use the <code>dmg</code> program from the
<a rel="external" href="http://github.com/planetbeing/libdmg-hfsplus">libdmg-hfsplus</a>
project.
<br />
The author clearly says:
</p>
<blockquote>
<p>THE CODE HEREIN SHOULD BE CONSIDERED HIGHLY EXPERIMENTAL</p>
</blockquote>
<p>
Note that the last commit of the project that I have tried doesn't compile
(somebody have reported
<a rel="external" href="http://github.com/planetbeing/libdmg-hfsplus/issues#issue/2">the bug</a>).
<br />
And the author of DMDirc report another bug in a previous version. But I have
tried its <a rel="external" href="http://shanemcc.co.uk/libdmg/">slightly modified version</a>, with success!
<br />
So, you should give it a try.
</p>
<p>
Here the added lines to our build script:
</p>
<pre># try to compress our dmg, if the the command <dmg> is available<br /># see http://github.com/planetbeing/libdmg-hfsplus<br /># and http://shanemcc.co.uk/libdmg/<br />if [ -x "./tools/dmg" ]; then<br /> # rename temporarily our dmg<br /> mv myapp-1.0.dmg myapp-1.0.dmg.pre<br /> # compress<br /> "./tools/dmg" dmg myapp-1.0.dmg.pre myapp-1.0.dmg<br /> rm -fv myapp-1.0.dmg.pre<br />fi</pre>
<p><em>
Note that I don't include the <code>dmg</code> program in the joined
archive, you have to download it yourself from the previous links, and
place it into the <code>tools</code> folder.
</em></p>
<h5>Create a dmg from Mac OSX</h5>
<p>
For information, to create a dmg directly on Mac OSX, there's the program
<a rel="external" href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/hdiutil.1.html">hdiutil</a>.
To create an auto open, compressed, dmg, from a folder named "myFolder",
something like this should work:
</p>
<pre>#!/bin/bash<br /><br />hdiutil create -volname "myapp-1.0" -fs HFS+ -srcfolder "myFolder" -format UDRW myapp-temp.dmg <br /><br />hdiutil convert myapp-temp.dmg -format UDZO -imagekey zlib-level=9 -o myapp.dmg<br /><br />rm -fv myapp-temp.dmg</pre>
<h5>Customize the dmg</h5>
<p>
It's possible to customize our dmg when it is mounted, i.e. the displayed
window, for example: icon size, background image, window size...
</p>
<p>
The idea is to create a read/write dmg on a mac, customize it manually, and then
make a copy of the file <a rel="external" href="http://en.wikipedia.org/wiki/.DS_Store">.DS_STORE</a>
and all the data. Later we will use these data
when we will create our dmg by script.
<br />
This part can only be done directly and manually on a Mac.
</p>
<h6>create a read/write disk image</h6>
<p>
On a Mac, open "Disk Utility" from the "Utilities" folder.
<br />
Click on "New Image".
<br />
Choose the <strong>same name</strong> as the future volume name of our dmg
(<code>MyApp</code>).
<br />
Don't use a journaled format, to be able to select a "small" size for
this dmg. The size doesn't matter, it have to be big enough to copy our
app and other data.
<br />
As <code>Image Format</code>, choose <code>read/write disk image</code>.
Then click on "create".
</p>
<h6>Add our data on the disk image</h6>
<p>
Now mount this dmg by clicking on it. And open it.
<br />
Copy our app in the open window.
We can copy the symbolic link to <code>/Applications</code> too, if it
exists. Otherwise we will create it in a terminal later.
</p>
<p>
Open a Terminal from the Utilities.
<br />
Go into the mounted disk image:
<br />
<kbd class="code-xterm">cd /Volumes/MyApp</kbd>
<br />
then create a folder named <code>.background</code>:
<br />
<kbd class="code-xterm">mkdir .background</kbd>
<br />
because the name begin by a dot (<code>.</code>), this folder is hidden, like on Linux.
<br />
copy in this folder our background image:
<br />
<kbd class="code-xterm">cp pathToOurImage/background.png /Volumes/MyApp/.background/</kbd>
<br />
And, if needed, create the symbolic link to /Applications:
<br />
<kbd class="code-xterm">ln -s /Applications /Volumes/MyApp/Applications</kbd>
</p>
<h6>Customize the disk image</h6>
<p>
Now give the focus to the window of the mounted disk, and open "Show View Options"
from the View menu.
<br />
Select <code>picture</code> for background, then click on the button.
<br />
We have to select the background.png file from the mounted disk, but because
this png is in a hidden folder we can't see it. Don't worry, type
<code>Apple+Shift+G</code>:
<br />
then type <code>/Volumes/MyApp/.background/</code>, then enter.
<br />
now select our png.
<br />
Choose the icon size, for example 128, arrange them by <code>none</code>,
then drag them where you want, and adapt the window size to our background.
</p>
<p>
Now close this window, and eject the mounted disk. Then mount it again, and open it.
</p>
<h6>Copy the resulting customization</h6>
<p>
In a terminal, copy the created file <code>.DS_STORE</code> at the root
of the mounted disk:
<br />
<kbd class="code-xterm">cp /Volumes/MyApp/.DS_STORE pathToSave/myDS_STORE</kbd>
</p>
<p>
Now we have finally all needed files. When we create our dmg from our
script, we just have to copy the file .DS_STORE and the folder
.background with the file background.png. The resulting dmg will have
the same customization.
<br />
Copy these data into our sources:
</p>
<pre> |- samples_chapter_4/<br /> |- data/<br /> |- bundle_skelet/<br /> |- dmg_extra_data/<br /> |- .background/<br /> |- background.png<br /> |- Applications<br /> |- .DS_STORE</pre>
<p>
And we add the following lines in our <code>build_mac.sh</code> script,
before creating the dmg:
</p>
<pre># copy eventual extra data, by example a symbolic link to /Applications<br />if [ $(ls -A "data/dmg_extra_data" | wc -c) -ne 0 ]; then<br /> cp -Rv data/dmg_extra_data/. "$TMP_DIR/myapp-1.0/"<br />fi</pre>
<p>
<em>
Note that this <strong>customization is dependent of the used volume name</strong>,
especially the background image, because the <code>.DS_STORE</code>
file uses absolute paths to the mounted disk, and the volume name is
part of it.
<br />
So, if you change the volume name, you have to recreate manually the customization...
</em>
</p>
<h5>Launch our build script</h5>
<p>To launch our script (on Linux), and create our dmg and tar.gz, in a terminal:</p>
<ul class="code-xterm">
<li><pre>cd samples_chapter_4</pre></li>
<li><pre>sh ./build_mac.sh</pre></li>
</ul>
<p>
And as result, we have finally the folder <code>MyApp.app</code>,
and the files <code>myapp-1.0.dmg</code> and <code>myapp-1.0.tar.gz</code>
in our main source folder.
</p>
</div>
<div class="howto-navigator-bottom">
<div class="howto-previous"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/Distribute-your-XULRunner-app-3-Windows" title="Go to chapter 3">< previous chapter</a></div>
<div class="howto-next"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/27/Distribute-your-XULRunner-app-5-Synthesis-all-platforms" title="Go to chapter 5">next chapter ></a></div><div class="clearer"> </div>
</div>
<div class="howto-author">
<p>Nicolas Martin</p>
</div>
<div class="howto-download-section">
<p>
You can download all the samples of this chapter 4 (Mac OSX) in the
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/dl.php?id=current_sample_4" class="howto-download-archive">samples_chapter_4.tar.gz</a>
archive.
</p>
</div>
<div class="howto-credits">
<p><em>
The myapp application, from
<a href="https://developer.mozilla.org/en/getting_started_with_xulrunner" rel="external">developer.mozilla.org</a>
is in the <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a>.
</em></p>
<p><em>
The icon used is from the
<a href="http://tango.freedesktop.org" rel="external">Tango Desktop Project</a>,
and is in the <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a>.
</em></p>
<p><em>
All other added data, and sample files, of this chapter 4, are in the
<a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a> too.
</em></p>
</div>
<div class="howto-footer">
<p>
The content of this article
is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>
</p>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width: 0;" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a>
</div>
</div>http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribute-your-XULRunner-app-4-Mac-OSX#comment-formhttp://joliclic.free.fr/blog/index.php?feed/atom/comments/83Distribuer votre appli XULRunner - 3 - Windowsurn:md5:cb731e299d00e37069a811826d42cdce2011-05-25T14:27:00+02:00joliclicmozilladist_xul_app_frhowtoxul <div class="howto">
<p><em>This post exists also in <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/Distribute-your-XULRunner-app-3-Windows">english</a></em></p>
<p><em>
Ce billet fait partie d'une <a href="http://joliclic.free.fr/blog/index.php?tag/dist_xul_app_fr">série</a> sur comment déployer une application XULRunner.
Voir le <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/20/Distribuer-votre-appli-XULRunner-1-Preambule">préambule</a> pour le contexte.
</em></p>
<h3><span>3 - Windows</span></h3>
<ul>
<li>3.1 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howtofr_dxr_3.1">Icônes pour Windows</a></li>
<li>3.2 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howtofr_dxr_3.2">Créer un lanceur (batch ou C)</a></li>
<li>3.3 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howtofr_dxr_3.3">Créer un script NSIS</a></li>
<li>3.4 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howtofr_dxr_3.4">Créer l'installeur</a></li>
</ul>
<div class="howto-section">
<h4><span>3.1 Icônes pour Windows</span></h4>
<p>
Nous avons besoin d'une icône au format .ico. Elle sera utilisée par nos
fenêtres, et par notre lanceur.
</p>
<p>
Il est facile de faire un fichier .ico sur Linux, nous avons juste besoin
du programme <code>icotool</code>, disponible dans le paquet <code>icoutils</code>.
<br />
Sur Debian/Ubuntu :
</p>
<p><code class="code-xterm-root">apt-get install icoutils</code></p>
<p>
Puis nous créons le fichier ico à partir de plusieurs png de différentes
tailles, 16x16 px, 32x32 px, et 48x48 px. Il serait possible d'utiliser
plus de png, avec différentes profondeurs de couleur par exemple.
</p>
<p><kbd class="code-xterm">icotool -c -o icon.ico icon16.png icon32.png icon48.png</kbd></p>
<p><em>
Vous trouverez un script bash avec cette ligne de commande dans l'archive
example de ce chapitre 3 (dans <code>samples_chapter_3/data/icons/</code>).
</em></p>
<p>
Pour utiliser notre icône pour nos fenêtres, plutôt que celle de firefox
ou de xulrunner, nous avons juste à mettre cette icône, renommée
<code>default.ico</code>, dans le dossier <code>icons/default</code> situé
dans notre dossier chrome principal.
<br />
Cette icône sera utilisée par tous les <code><window></code> XUL
sans un attribut <code>id</code>.
<br />
Mais le <code><window></code> XUL principal de myapp a l'attribut
<code>id="main"</code>, donc pour cette fenêtre nous devons avoir une
icône nommée <code>main.ico</code>, située dans le même dossier.
</p>
<pre> |- samples_chapter_3/<br /> |- myapp/<br /> |- chrome<br /> |- icons/<br /> |- default/<br /> |- default.ico<br /> |- main.ico</pre>
</div>
<div class="howto-section">
<h4><span>3.2 Créer un lanceur (batch ou C)</span></h4>
<p>
Le lanceur le plus simple est un <strong>script batch</strong>. Voici le
contenu de <code><strong>myapp.bat</strong></code> :
</p>
<pre>set CUR_DIR=%~dp0<br /><br />START firefox -app $CUR_DIR\application.ini</pre>
<p>
Ce fichier doit être placé dans le dossier principal de notre appli.
Ça marche, il lance notre appli via le fichier application.ini de notre
appli.
<br />
Mais il se passe quelque chose de gênant, une fenêtre noire de commande
est également ouverte.
</p>
<p>
Pour éviter ce defaut, nous allons créer un <strong>lanceur en C</strong>.
</p>
<p>
Nous utiliserons le code de <strong><a href="http://joliclic.free.fr/mozilla/xal/" rel="external">XAL</a></strong>
(XUL Application Launcher). C'est un programme léger en C, sous licence
<a href="http://www.opensource.org/licenses/mit-license.php" rel="external">MIT</a>,
il lance une application XULRunner avec Firefox, avec l'argument <code>-app</code>
et <code>application.ini</code>.
Il doit être placé dans le dossier principal de l'application (comme le batch
précédent). Bonus, il prend en charge d'éventuels arguments supplémentaires
(comme <code>-jsconsole</code>), quand il est utilisé en ligne de commande.
Et nous pouvons ajouter notre icône, et quelques autres informations sur
l'application, via un fichier .rc.
</p>
<p>
Cet exécutable peut etre compilé avec n'importe quel compilateur C, son
code est indépendant du code de Mozilla. Personnellement je le compile
avec <a href="http://www.mingw.org/" rel="external">MinGW</a>, sur Linux,
avec succés.
Si vous voulez utiliser un autre compilateur, éditer le fichier build,
et adapter les variables <code>CC</code> et <code>RESCOMP</code>.
</p>
<p>pour installer MinGW sur Debian/Ubuntu:</p>
<p><code class="code-xterm-root">apt-get install mingw32</code></p>
<p>
Je ne publie pas ici le code source C, vous le trouverez dans l'archive
relative à ce chapitre, où dans sa
<a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">page dédiée</a>.
</p>
<p>
Nous pouvons <strong>personnaliser</strong> ce lanceur, en utilisant un
fichier
<a href="http://msdn.microsoft.com/en-us/library/aa381043%28v=VS.85%29.aspx" rel="external">resource</a>
(<code>.rc</code>), insérer notre icône et spécifier quelques informations
(vendeur, nom de l'appli, version,...).
</p>
<p>
Voici le contenu du fichier myapp-res.rc :
</p>
<pre>APPLICATION_ICON ICON "myapp.ico"<br /><br />1 VERSIONINFO<br /> FILEVERSION 0,1,0,0<br /> PRODUCTVERSION 0,0,0,0<br />BEGIN<br /> BLOCK "StringFileInfo"<br /> BEGIN<br /> BLOCK "000004B0"<br /> BEGIN<br /> VALUE "Comments", "Published under the MPL 1.1/GPL 2.0/LGPL 2.1 licenses"<br /> VALUE "CompanyName", "John Doe Organization"<br /> VALUE "FileDescription", "MyApp"<br /> VALUE "FileVersion", "1.0.0"<br /> VALUE "InternalName", "MyApp"<br /> VALUE "LegalCopyright", "(c) 2010 John Doe"<br /> VALUE "ProductName", "MyApp"<br /> VALUE "ProductVersion", "0.0.0"<br /> END<br /> END<br /> BLOCK "VarFileInfo"<br /> BEGIN<br /> VALUE "Translation", 0x000, 1200<br /> END<br />END</pre>
<p>Quelques détails sur ce contenu :</p>
<p>
La ligne <code>APPLICATION_ICON <strong>ICON</strong> "myapp.ico"</code>
insère notre icône
</p>
<p>
Les entrées <a href="http://msdn.microsoft.com/en-us/library/aa381058%28v=VS.85%29.aspx" rel="external"><code><strong>FILEVERSION</strong></code>
et <code><strong>PRODUCTVERSION</strong></code></a>
utilisent un format spécial, en résumé
<code>entier virgule entier virgule entier virgule entier</code>.
</p>
<p>
La ligne <code>VALUE "Translation", 0x000, 1200</code> spécifie que les
chaînes (string) utilisées
dans les champs d'information sont en Unicode.
</p>
<p>
Nous pouvons compiler ce lanceur, en utilisant notre fichier resource, avec
le script <code>build.sh</code> fourni avec XAL :
</p>
<p>
<kbd class="code-xterm">sh build.sh myapp myapp-res.rc</kbd>
</p>
<p>
Après compilation, la taille du fichier créé est de ~21Ko, donc vraiment
léger (notez que le poids de l'icône est incluse).
</p>
</div>
<div class="howto-section">
<h4><span>3.3 Créer un script NSIS</span></h4>
<p>
Il existe plusieurs solution pour créer des installeurs pour Windows.
Ici nous utiliserons <a href="http://nsis.sourceforge.net/" rel="external"><strong>NSIS</strong></a>,
car : il est open source, nous pouvons construire notre installeur
depuis Linux, et qu'il est facile et puissant.
<br />
Firefox lui-même utilise NSIS pour son installeur Windows.
</p>
<p>
Pour installer les outils NSIS sur Debian/Ubuntu :
</p>
<p><code class="code-xterm-root">apt-get install nsis</code></p>
<p>
NSIS utilise son propre language de script pour créer les installeurs.
Nous pouvons utiliser des pages par défaut, en créer de nouvelles personnalisées,
gérer les fichiers lors de l'installation/désinstallation, agir sur le Registre
Windows,...
<br />
Je ne vais pas faire une documentation complète sur NSIS ici, voyez leur
<a href="http://nsis.sourceforge.net/Main_Page" rel="external">wiki</a>,
il y a beaucoup d'explications et d'exemples.
<br />
Vous devriez avoir des exemples et une doc disponibles en local, une fois
nsis installé, dans
<code>/usr/share/doc/nsis/Examples</code> et <code>/usr/share/doc/nsis/Doc</code>.
</p>
<p>
Mais voilà le principal script proposé, et j'explique par la suite quelles actions
sont réalisées :
</p>
<pre>!define PRODUCT_NAME "MyApp"<br />!define PRODUCT_INTERNAL_NAME "myapp"<br />!define PRODUCT_VERSION "1.0"<br />!define PRODUCT_WIN_VERSION "1.0.0.0"<br /><br />!define MESSAGEWINDOW_NAME "${PRODUCT_NAME}MessageWindow"<br /><br />!define HKEY_ROOT "HKLM"<br />!define UN_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"<br /><br />!define LICENSE_PATH "${PRODUCT_INTERNAL_NAME}\LICENSE.txt"<br /><br />!define INSTALLER_NAME "${PRODUCT_NAME}-${PRODUCT_VERSION}-install.exe"<br />!define TMP_UNINSTALL_EXE "${PRODUCT_INTERNAL_NAME}_uninstall.exe"<br /><br />;--------------------------------<br />;Variables<br /><br />; The name of the product installed<br />Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"<br /><br />; The file to write<br />OutFile "${INSTALLER_NAME}"<br /><br />SetCompressor /final /solid lzma<br />ShowInstDetails show<br />ShowUninstDetails show<br /><br />; The default installation directory<br />InstallDir $PROGRAMFILES\${PRODUCT_NAME}<br /><br />; Request application privileges for Windows Vista<br />RequestExecutionLevel admin<br /><br />Var Shortcuts_Dialog<br />Var Shortcuts_Label<br />Var Shortcuts_SM_Checkbox<br />Var Shortcuts_SM_Checkbox_State<br />Var Shortcuts_D_Checkbox<br />Var Shortcuts_D_Checkbox_State<br /><br />Var Previous_Uninstall<br />Var Previous_Uninstall_dir<br />Var TempUninstallPath<br /><br />!include "MUI2.nsh"<br />!include "FileFunc.nsh"<br /><br />VIProductVersion "${PRODUCT_WIN_VERSION}"<br /><br />VIAddVersionKey "ProductName" "${PRODUCT_NAME}"<br />;VIAddVersionKey "CompanyName" "${CompanyName}"<br />;VIAddVersionKey "LegalTrademarks" "${BrandShortName} is a Trademark of"<br />;VIAddVersionKey "LegalCopyright" "${CompanyName}"<br />VIAddVersionKey "LegalCopyright" ""<br />VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"<br />VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"<br />VIAddVersionKey "FileDescription" "${PRODUCT_NAME} Installer"<br />VIAddVersionKey "OriginalFilename" "${INSTALLER_NAME}"<br /><br />!define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /><br />;--------------------------------<br />; Pages<br /><br /> !insertmacro MUI_PAGE_WELCOME<br /> !insertmacro MUI_PAGE_LICENSE "${LICENSE_PATH}"<br /> !insertmacro MUI_PAGE_DIRECTORY<br /> Page custom onShortcutsPageCreate<br /> !insertmacro MUI_PAGE_INSTFILES<br /> !insertmacro MUI_PAGE_FINISH<br /> <br /> !insertmacro MUI_UNPAGE_WELCOME<br /> !insertmacro MUI_UNPAGE_CONFIRM<br /> !insertmacro MUI_UNPAGE_INSTFILES<br /> <br /> <br /> !insertmacro MUI_LANGUAGE "English"<br /> !insertmacro MUI_LANGUAGE "French"<br /><br /> !include ./l10n/fr.nsh<br /> !include ./l10n/en_US.nsh<br /><br /><br />;--------------------------------<br /><br />Function .onInit<br /> ; an eventual previous version of the app should not be currently running.<br /> ; Abort if any.<br /> ; Explanation, when the application is running, a window with the className<br /> ; productnameMessageWindow exists<br /> FindWindow $0 "${MESSAGEWINDOW_NAME}"<br /> StrCmp $0 0 +3<br /> MessageBox MB_OK|MB_ICONEXCLAMATION "${PRODUCT_NAME} is running. Please close it first" /SD IDOK<br /> Abort<br /> <br /> StrCpy $Shortcuts_SM_Checkbox_State 1<br /> StrCpy $Shortcuts_D_Checkbox_State 1<br />FunctionEnd<br /><br />Function un.onInit<br /> ; see Function .onInit<br /> FindWindow $0 "${MESSAGEWINDOW_NAME}"<br /> StrCmp $0 0 +3<br /> MessageBox MB_OK|MB_ICONEXCLAMATION "${PRODUCT_NAME} is running. Please close it first" /SD IDOK<br /> Abort<br />FunctionEnd<br /><br />; custom page creation, for the shortcuts installation, using nsDialog<br />Function onShortcutsPageCreate<br /> !insertmacro MUI_HEADER_TEXT $(l10n_SHORTCUTS_PAGE_TITLE) \<br /> $(l10n_SHORTCUTS_PAGE_SUBTITLE)<br /> <br /> nsDialogs::Create 1018<br /> Pop $Shortcuts_Dialog<br /> <br /> ${If} $Shortcuts_Dialog == error<br /> Abort<br /> ${EndIf}<br /><br /> ${NSD_CreateLabel} 0 6 100% 12u $(l10n_CREATE_ICONS_DESC)<br /> Pop $Shortcuts_Label<br /><br /> ${NSD_CreateCheckbox} 15u 20u 100% 10u $(l10n_ICONS_STARTMENU)<br /> Pop $Shortcuts_SM_Checkbox<br /> GetFunctionAddress $0 OnSMCheckbox<br /> nsDialogs::OnClick $Shortcuts_SM_Checkbox $0<br /> <br /> ${If} $Shortcuts_SM_Checkbox_State == ${BST_CHECKED}<br /> ${NSD_Check} $Shortcuts_SM_Checkbox<br /> ${EndIf}<br /><br /> ${NSD_CreateCheckbox} 15u 40u 100% 10u $(l10n_ICONS_DESKTOP)<br /> Pop $Shortcuts_D_Checkbox<br /> GetFunctionAddress $0 OnDCheckbox<br /> nsDialogs::OnClick $Shortcuts_D_Checkbox $0<br /><br /> ${If} $Shortcuts_D_Checkbox_State == ${BST_CHECKED}<br /> ${NSD_Check} $Shortcuts_D_Checkbox<br /> ${EndIf}<br /><br /> nsDialogs::Show<br />FunctionEnd<br /><br />; event when the Start Menu shortcut is (un)checked in the custom page<br />Function OnSMCheckbox<br /> ${NSD_GetState} $Shortcuts_SM_Checkbox $Shortcuts_SM_Checkbox_State<br /> Pop $0 # HWND<br />FunctionEnd<br /><br />; event when the Desktop shortcut is (un)checked in the custom page<br />Function OnDCheckbox<br /> ${NSD_GetState} $Shortcuts_D_Checkbox $Shortcuts_D_Checkbox_State<br /> Pop $0 # HWND<br />FunctionEnd<br /><br />Function WriteUninstallReg<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayName" \<br /> "${PRODUCT_NAME} (${PRODUCT_VERSION})"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "UninstallString" \<br /> "$INSTDIR\uninstall.exe"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "QuietUninstallString" \<br /> "$INSTDIR\uninstall.exe /S"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "InstallLocation" \<br /> "$INSTDIR"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayIcon" \<br /> "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayVersion" \<br /> "${PRODUCT_VERSION}"<br /> <br /> ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2<br /> IntFmt $0 "0x%08X" $0<br /> WriteRegDWORD ${HKEY_ROOT} ${UN_KEY} "EstimatedSize" "$0"<br />FunctionEnd<br /><br />; The stuff to install<br />Section ""<br /> ; uninstall an eventual previous installation<br /> ReadRegStr $Previous_Uninstall ${HKEY_ROOT} ${UN_KEY} "UninstallString"<br /> ClearErrors<br /> ${If} $Previous_Uninstall != ""<br /> StrCpy $Previous_Uninstall_dir $Previous_Uninstall<br /> ${GetParent} $Previous_Uninstall $Previous_Uninstall_dir<br /> <br /> IfFileExists "$Previous_Uninstall" myUninstallPrevious myInstall<br /> ${Else}<br /> goto myInstall<br /> ${EndIf}<br /> <br /> myUninstallPrevious:<br /> ; copy the previous uninstaller into TEMP<br /> ClearErrors<br /> StrCpy $TempUninstallPath "$TEMP\${TMP_UNINSTALL_EXE}"<br /> CopyFiles /SILENT "$Previous_Uninstall" "$TempUninstallPath"<br /> IfErrors myInstall<br /> <br /> ClearErrors<br /> ExecWait '"$TempUninstallPath" /S _?=$Previous_Uninstall_dir'<br /> <br /> ClearErrors<br /> Delete "$TempUninstallPath"<br /> <br /> ;MessageBox MB_OK "UNINSTALL: finished"<br /> <br /> myInstall:<br /> SetOutPath $INSTDIR<br /> <br /> ; copy the files<br /> File /r ${PRODUCT_INTERNAL_NAME}\*<br /> <br /> WriteUninstaller "uninstall.exe"<br /> <br /> Call WriteUninstallReg<br /> <br /> ${If} $Shortcuts_SM_Checkbox_State == ${BST_CHECKED}<br /> CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" \<br /> "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /> ${EndIf}<br /> <br /> ${If} $Shortcuts_D_Checkbox_State == ${BST_CHECKED}<br /> CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" \<br /> "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /> ${EndIf}<br />SectionEnd<br /><br />;--------------------------------<br />; Uninstaller<br /><br />Section "Uninstall"<br /> MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR" /SD IDOK<br /> ; Remove installed files and uninstaller<br /> !include ./uninstall_files.nsi<br /> Delete "$INSTDIR\uninstall.exe"<br /> <br /> ; remove installed directories<br /> !include ./uninstall_dirs.nsi<br /> RMDir /r "$INSTDIR\extensions"<br /> <br /> ; Remove shortcuts, if any<br /> Delete "$SMPROGRAMS\${PRODUCT_NAME}.lnk"<br /> Delete "$DESKTOP\${PRODUCT_NAME}.lnk"<br /> ;TODO remove eventual quicklaunch Too<br /> <br /> ; Remove the installation directory used (if empty)<br /> RMDir "$INSTDIR"<br /> <br /> ; and delete the registry key for uninstall<br /> DeleteRegKey ${HKEY_ROOT} ${UN_KEY}<br />SectionEnd</pre>
<p>Quelques explications sur ce script :</p>
<p>Il utilise le thème par défaut "modern". Mais il serait possible de le personnaliser.</p>
<p>
Certaines parties sont localisées ("traduites"), en insérant d'autres
scripts nsis (décrits plus loin).
</p>
<p>
Il utilise quelques pages nsis par défaut, et une personnalisée pour la création
des raccourcis sur le bureau et dans le menu démarrez.
</p>
<p>
L'installation et la désinstallation sont annulées si notre application est
actuellement lancée.
<br />
En fait, quand notre application est en fonctionnement, XULRunner/Firefox crée
une fenêtre Windows native avec une classe <code>MyAppMessageWindow</code>,
le nom de cette classe est la valeur du champ <code>Name</code>
dans <code>application.ini</code> concaténée avec "MessageWindow".
<br />
Le script vérifie juste si une telle fenêtre avec ce nom de classe est ouverte,
et annule le traitement.
</p>
<p>
Il crée quelques entrées minimales dans La Base de Registre, pour permettre
la désinstallation du programme avec l'outil "ajouter/supprimer des programmes"
de Windows.
</p>
<p>
Si notre appli est déjà installée, la précédente version est désinstallée
avant la nouvelle installation, en utilisant le désinstalleur précédent.
Et, pour être plus précis, ce précédent désinstalleur est copié et lancé
depuis le dossier "Temp" de Windows.
</p>
<p>
Pour la désinstallation, nous avons besoin de la liste complète des fichiers
et dossiers installés.
Ces listes seront créés dynamiquement plus tard, pour le moment dans ce script
nous les insérons en tant que scripts nsis supplémentaires
(<code>uninstall_files.nsi</code> et <code>uninstall_dirs.nsi</code>).
</p>
<p>
Maintenant voici le contenu de l'un des scripts localisés ("traduit"), en_US.nsh :
</p>
<pre>;!!! IMPORTANT: this must file must be edited with ANSI charset (in fact the<br />; Windows CP-1252 charset), but if there's no special character, UTF-8 is ok,<br />; because it's a subset ;)<br /><br />LangString l10n_SHORTCUTS_PAGE_TITLE ${LANG_ENGLISH} \<br /> "Set Up Shortcuts"<br />LangString l10n_SHORTCUTS_PAGE_SUBTITLE ${LANG_ENGLISH} \<br /> "Create Program Icons"<br />LangString l10n_CREATE_ICONS_DESC ${LANG_ENGLISH} \<br /> "Create icons for ${PRODUCT_NAME}:"<br />LangString l10n_ICONS_DESKTOP ${LANG_ENGLISH} \<br /> "On my &Desktop"<br />LangString l10n_ICONS_STARTMENU ${LANG_ENGLISH} \<br /> "In my &Start Menu Programs folder"</pre>
<p>
Nous devons faire attention à l'encodage des ces fichiers, celui-ci en anglais
ne devrait pas poser de problème, mais celui en français doit être édité
avec le charset Windows <code>CP-1252</code> par exemple.
Ces variables sont utilisées simplement dans notre script principal avec,
par exemple, <code>$(l10n_SHORTCUTS_PAGE_TITLE)</code>.
</p>
<p>
Notez que l'esperluette <code>&</code> dans les définitions de chaîne
définissent des raccourcis clavier pour les contrôles dans l'interface
utilisateur, ici il y a <code>alt+D</code> et <code>alt+S</code>.
</p>
</div>
<div class="howto-section">
<h4><span>3.4 Créer l'installeur</span></h4>
<p>
Nous avons maintenant tous les fichiers pour créer l'installeur.
Voyons l'arborescence de nos sources :
</p>
<pre> |-samples_chapter_3<br /> |- data/<br /> |- icons/<br /> |- icon.ico<br /> |- myapp.bat<br /> |- myapp-res.rc<br /> |- myapp/<br /> |- win<br /> |- nsis/<br /> |- l10n/<br /> |- en_US.nsh<br /> |- fr.nsh<br /> |- myapp.nsi<br /> |- xal-src/<br /> |- build.sh<br /> |- clean.sh<br /> |- main.c</pre>
<p>
Nous devons construire notre lanceur à partir des sources C et de nos
données (icône et resource).
Et créer 2 scripts nsis additionnels, pour lister les fichiers de notre appli.
Puis nous pourrons créer notre installeur avec <code>makensis</code>.
Nous allons faire celà dans un dossier temporaire. Voici le script, nommé
<code>build_win.sh</code> :
</p>
<pre>#!/bin/bash<br /><br /># exit the script on errors<br />set -e<br /><br />TMP_DIR=./tmp<br /><br /># get the absolute path of our TMP_DIR folder<br />TMP_DIR=$(readlink -f -- "$TMP_DIR")<br /><br /># re-create the TMP_DIR folder<br />rm -rfv "$TMP_DIR"<br />mkdir -v "$TMP_DIR"<br /><br /># copy our app<br />cp -rv myapp "$TMP_DIR/"<br /><br /># copy the XAL launcher sources<br />cp -rv win/xal-src "$TMP_DIR/xal"<br /># and our icon and resource file<br />cp -v data/icons/icon.ico "$TMP_DIR/xal/myapp.ico"<br />cp -v data/myapp-res.rc "$TMP_DIR/xal/"<br /><br /># build the launcher<br />cd "$TMP_DIR/xal/"<br />sh build.sh myapp myapp-res.rc<br />cd -<br /><br /># copy the launchers in the right folder<br />cp -v data/myapp.bat "$TMP_DIR/myapp/"<br />cp -v "$TMP_DIR/xal/myapp.exe" "$TMP_DIR/myapp/"<br /><br /># delete the xal sources<br />rm -rv "$TMP_DIR/xal"<br /><br /># create the nsis script listing the files to unsinstall<br />cd "$TMP_DIR/myapp"<br />find . -maxdepth 1 -type f > ../uninstall_files.nsi<br />sed -i -r "s|^\./(.*)$| Delete \"\$INSTDIR\\\\\1\"|" ../uninstall_files.nsi<br /><br /># and the list of directory<br />ls -d */ > ../uninstall_dirs.nsi<br />sed -i -r "s|^(.*)/$| RMDir /r \"\$INSTDIR\\\\\1\"|" ../uninstall_dirs.nsi<br />cd -<br /><br /># copy the other nsis scripts<br />cp -rv win/nsis/* "$TMP_DIR/"<br /><br /># and create the installer<br />makensis "$TMP_DIR/myapp.nsi"<br /><br /># finally, copy our installer in the root sources dir<br />cp -v "$TMP_DIR/MyApp-1.0-install.exe" ./<br /><br />echo "Windows installer for myapp created."</pre>
<p>Pour lancer ce script, dans un terminal :</p>
<ul class="code-xterm">
<li><pre>cd samples_chapter_3</pre></li>
<li><pre>sh ./build_win.sh</pre></li>
</ul>
<p>
Et en résultat nous avons finalement le fichier
<code>MyApp-1.0-install.exe</code> dans le dossier
<code>samples_chapter_3</code> :) .
</p>
</div>
<div class="howto-navigator-bottom">
<div class="howto-previous"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/24/Distribuer-votre-appli-XULRunner-24-Empaqueter-un-rpm" title="Aller au chapitre 2.4">< chapitre précédent</a></div>
<div class="howto-next"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribuer-votre-appli-XULRunner-4-Mac-OSX" title="Aller au chapitre 4">chapitre suivant ></a></div>
<div class="clearer"> </div>
</div>
<div class="howto-author">
<p>Nicolas Martin</p>
</div>
<div class="howto-download-section">
<p>
Vous pouvez télécharger tous les exemples de ce chapitre 3 (Windows) dans l'archive
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/dl.php?id=current_sample_3" class="howto-download-archive">
samples_chapter_3.tar.gz
</a>.
</p>
</div>
<div class="howto-credits">
<p><em>
L'application myapp, de
<a href="https://developer.mozilla.org/en/getting_started_with_xulrunner" rel="external">developer.mozilla.org</a>,
est dans le <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domaine Public</a>.
</em></p>
<p><em>
L'icône utilisée est issue du
<a href="http://tango.freedesktop.org" rel="external">Tango Desktop Project</a>,
et est dans le <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domaine Public</a>.
</em></p>
<p><em>
le lanceur C
<a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">XUL App Launcher</a> (XAL)
est sous <a href="http://www.opensource.org/licenses/mit-license.php" rel="external">licence MIT</a>.
</em></p>
<p><em>
Toutes les autres données ajoutées, et les fichiers en exemple, de ce chapitre 3, sont dans le
<a href="http://wiki.creativecommons.org/Public_domain" rel="external">Domain Public</a> également.
</em></p>
</div>
<div class="howto-footer">
<p>
Le contenu de cet article est sous les termes de la licence
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>
</p>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width: 0;" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a>
</div>
</div>http://joliclic.free.fr/blog/index.php?post/2011/05/25/Distribuer-votre-appli-XULRunner-3-Windows#comment-formhttp://joliclic.free.fr/blog/index.php?feed/atom/comments/82Distribute your XULRunner app - 3 - Windowsurn:md5:dc9f42261ed18295b665b4bb5442baac2011-05-25T14:25:00+02:00joliclicmozilladist_xul_app_enhowtoxul <div class="howto">
<p><em>Ce billet existe aussi en <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/Distribuer-votre-appli-XULRunner-3-Windows">français</a></em></p>
<p><em>
This post is part of a <a href="http://joliclic.free.fr/blog/index.php?tag/dist_xul_app_en">series</a> about how to package a XULRunner application.<br />
See the <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/20/Distribute-your-XULRunner-app-1-Preamble">preamble</a> for the context case.
</em></p>
<h3><span>3 - Windows</span></h3>
<ul>
<li>3.1 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howto_dxr_3.1">Icons for Windows</a></li>
<li>3.2 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howto_dxr_3.2">Create a launcher (batch or C)</a></li>
<li>3.3 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howto_dxr_3.3">Create a NSIS script</a></li>
<li>3.4 <a href="http://joliclic.free.fr/blog/index.php?post/2011/05/25/#howto_dxr_3.4">Create the installer</a></li>
</ul>
<div class="howto-section">
<h4><span>3.1 Icons for Windows</span></h4>
<p>
We need a icon in .ico format. It will be used by ours windows, and by
our launcher.
</p>
<p>
It's easy to make a .ico file on Linux, we just need to install the
program <code>icotool</code>, from the <code>icoutils</code> package.
<br />
On Debian/Ubuntu:
</p>
<p><code class="code-xterm-root">apt-get install icoutils</code></p>
<p>
Then we create a ico file from several png at different size,
16x16 px, 32x32 px, and 48x48 px. It would be possible to add more png,
with different color depth as well.
</p>
<p><kbd class="code-xterm">icotool -c -o icon.ico icon16.png icon32.png icon48.png</kbd></p>
<p><em>
You can find a bash script with this command line in the sample archive
related to this chapter 3 (in <code>samples_chapter_3/data/icons/</code>).
</em></p>
<p>
To add our icon to our windows, rather than the firefox or xulrunner one,
we have just to put this icon, renamed as <code>default.ico</code>, into a
<code>icons/default</code> folder situated in our main chrome folder.
<br />
This icon will be used by all XUL <code><window></code> without an
<code>id</code> attribute.
<br />
But the main XUL <code><window></code> of myapp has the attribute
<code>id="main"</code>, so for this window we must have an icon named
<code>main.ico</code>, situated in the same folder:
</p>
<pre> |- samples_chapter_3/<br /> |- myapp/<br /> |- chrome<br /> |- icons/<br /> |- default/<br /> |- default.ico<br /> |- main.ico</pre>
</div>
<div class="howto-section">
<h4><span>3.2 Create a launcher (batch or C)</span></h4>
<p>
The simplest launcher is a <strong>batch script</strong>. Here the content
of <code><strong>myapp.bat</strong></code>:
</p>
<pre>set CUR_DIR=%~dp0<br /><br />START firefox -app $CUR_DIR\application.ini</pre>
<p>
This file must be placed into the main folder of our app.
This works, it launches our app via the application.ini file of our app.
<br />
But there's something annoying, a black command window is opened too.
</p>
<p>
To avoid the previously mentioned default, we will create a
<strong>launcher in C</strong>.
</p>
<p>
We use the code of <strong><a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">XAL</a></strong>
(XUL Application Launcher). This is a light C program,
<a href="http://www.opensource.org/licenses/mit-license.php" rel="external">MIT</a>
licensed, it launches
a XULRunner application with Firefox, with the <code>-app</code> argument and the
<code>application.ini</code>.
It must be placed in the main application folder (like the previous batch).
Bonus, it handles additional arguments (like <code>-jsconsole</code>) if
used in command line. And we can add our icon, and some application
information via a .rc file.
</p>
<p>
This executable can be compiled with any C compiler, its code is
independent of the Mozilla code. I personaly compile it with
<a href="http://www.mingw.org/" rel="external">MinGW</a>, on Linux, with
success. If you plan to use another compiler, edit the build file, and
adapt the <code>CC</code> and <code>RESCOMP</code> variables.
</p>
<p>To install MinGW on Debian/Ubuntu:</p>
<p><code class="code-xterm-root">apt-get install mingw32</code></p>
<p>
I don't publish here the C source of this code, you'll find it in the
archive related to this chapter 3, or in its
<a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">dedicated page</a>.
</p>
<p>
We can <strong>customize</strong> this launcher, using a
<a href="http://msdn.microsoft.com/en-us/library/aa381043%28v=VS.85%29.aspx" rel="external">resource</a>
(<code>.rc</code>) file, insert our icon and specify some
information (vendor, app name, version,...).
</p>
<p>
Here the content of the myapp-res.rc file:
</p>
<pre>APPLICATION_ICON ICON "myapp.ico"<br /><br />1 VERSIONINFO<br /> FILEVERSION 0,1,0,0<br /> PRODUCTVERSION 0,0,0,0<br />BEGIN<br /> BLOCK "StringFileInfo"<br /> BEGIN<br /> BLOCK "000004B0"<br /> BEGIN<br /> VALUE "Comments", "Published under the MPL 1.1/GPL 2.0/LGPL 2.1 licenses"<br /> VALUE "CompanyName", "John Doe Organization"<br /> VALUE "FileDescription", "MyApp"<br /> VALUE "FileVersion", "1.0.0"<br /> VALUE "InternalName", "MyApp"<br /> VALUE "LegalCopyright", "(c) 2010 John Doe"<br /> VALUE "ProductName", "MyApp"<br /> VALUE "ProductVersion", "0.0.0"<br /> END<br /> END<br /> BLOCK "VarFileInfo"<br /> BEGIN<br /> VALUE "Translation", 0x000, 1200<br /> END<br />END</pre>
<p>Some details about this content:</p>
<p>
The line <code>APPLICATION_ICON <strong>ICON</strong> "myapp.ico"</code>
integrate our icon
</p>
<p>
The <a href="http://msdn.microsoft.com/en-us/library/aa381058%28v=VS.85%29.aspx" rel="external"><code><strong>FILEVERSION</strong></code> and <code><strong>PRODUCTVERSION</strong></code></a>
entries use a special format, in short <code>integer comma integer comma integer comma integer</code>.
</p>
<p>
The line <code>VALUE "Translation", 0x000, 1200</code> specify that the
strings for information use Unicode.
</p>
<p>
We can compile this launcher, using our resource file, with the
<code>build.sh</code> script distributed with XAL:
</p>
<p>
<kbd class="code-xterm">sh build.sh myapp myapp-res.rc</kbd>
</p>
<p>
After compilation, the size of the result file is ~21Kb, so very small
(note that the size of the icon is included).
</p>
</div>
<div class="howto-section">
<h4><span>3.3 Create a NSIS script</span></h4>
<p>
There are several solution to create Windows installers. Here, we will
use <a href="http://nsis.sourceforge.net/" rel="external"><strong>NSIS</strong></a>,
because: it's open source, we can build our installer from Linux, and
it's easy and powerful.
<br />
Firefox itself use NSIS for its Windows installer.
</p>
<p>
To install NSIS tools on Debian/Ubuntu:
</p>
<p><code class="code-xterm-root">apt-get install nsis</code></p>
<p>
NSIS uses its own script language to create installers.
We can use some default pages, create some custom ones, manage files (un)installation,
act on the Windows Registry,...
<br />
I will not do a complete NSIS documentation here, see their
<a href="http://nsis.sourceforge.net/Main_Page" rel="external">wiki</a> for that,
there's a lot of explanations and examples.
<br />
You should have some local examples and doc after installing nsis,
in <code>/usr/share/doc/nsis/Examples</code> and <code>/usr/share/doc/nsis/Doc</code>.
</p>
<p>
But here's the main proposed script here, and I will explain what actions
are performed after:
</p>
<pre>!define PRODUCT_NAME "MyApp"<br />!define PRODUCT_INTERNAL_NAME "myapp"<br />!define PRODUCT_VERSION "1.0"<br />!define PRODUCT_WIN_VERSION "1.0.0.0"<br /><br />!define MESSAGEWINDOW_NAME "${PRODUCT_NAME}MessageWindow"<br /><br />!define HKEY_ROOT "HKLM"<br />!define UN_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"<br /><br />!define LICENSE_PATH "${PRODUCT_INTERNAL_NAME}\LICENSE.txt"<br /><br />!define INSTALLER_NAME "${PRODUCT_NAME}-${PRODUCT_VERSION}-install.exe"<br />!define TMP_UNINSTALL_EXE "${PRODUCT_INTERNAL_NAME}_uninstall.exe"<br /><br />;--------------------------------<br />;Variables<br /><br />; The name of the product installed<br />Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"<br /><br />; The file to write<br />OutFile "${INSTALLER_NAME}"<br /><br />SetCompressor /final /solid lzma<br />ShowInstDetails show<br />ShowUninstDetails show<br /><br />; The default installation directory<br />InstallDir $PROGRAMFILES\${PRODUCT_NAME}<br /><br />; Request application privileges for Windows Vista<br />RequestExecutionLevel admin<br /><br />Var Shortcuts_Dialog<br />Var Shortcuts_Label<br />Var Shortcuts_SM_Checkbox<br />Var Shortcuts_SM_Checkbox_State<br />Var Shortcuts_D_Checkbox<br />Var Shortcuts_D_Checkbox_State<br /><br />Var Previous_Uninstall<br />Var Previous_Uninstall_dir<br />Var TempUninstallPath<br /><br />!include "MUI2.nsh"<br />!include "FileFunc.nsh"<br /><br />VIProductVersion "${PRODUCT_WIN_VERSION}"<br /><br />VIAddVersionKey "ProductName" "${PRODUCT_NAME}"<br />;VIAddVersionKey "CompanyName" "${CompanyName}"<br />;VIAddVersionKey "LegalTrademarks" "${BrandShortName} is a Trademark of"<br />;VIAddVersionKey "LegalCopyright" "${CompanyName}"<br />VIAddVersionKey "LegalCopyright" ""<br />VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"<br />VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"<br />VIAddVersionKey "FileDescription" "${PRODUCT_NAME} Installer"<br />VIAddVersionKey "OriginalFilename" "${INSTALLER_NAME}"<br /><br />!define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /><br />;--------------------------------<br />; Pages<br /><br /> !insertmacro MUI_PAGE_WELCOME<br /> !insertmacro MUI_PAGE_LICENSE "${LICENSE_PATH}"<br /> !insertmacro MUI_PAGE_DIRECTORY<br /> Page custom onShortcutsPageCreate<br /> !insertmacro MUI_PAGE_INSTFILES<br /> !insertmacro MUI_PAGE_FINISH<br /> <br /> !insertmacro MUI_UNPAGE_WELCOME<br /> !insertmacro MUI_UNPAGE_CONFIRM<br /> !insertmacro MUI_UNPAGE_INSTFILES<br /> <br /> <br /> !insertmacro MUI_LANGUAGE "English"<br /> !insertmacro MUI_LANGUAGE "French"<br /><br /> !include ./l10n/fr.nsh<br /> !include ./l10n/en_US.nsh<br /><br /><br />;--------------------------------<br /><br />Function .onInit<br /> ; an eventual previous version of the app should not be currently running.<br /> ; Abort if any.<br /> ; Explanation, when the application is running, a window with the className<br /> ; productnameMessageWindow exists<br /> FindWindow $0 "${MESSAGEWINDOW_NAME}"<br /> StrCmp $0 0 +3<br /> MessageBox MB_OK|MB_ICONEXCLAMATION "${PRODUCT_NAME} is running. Please close it first" /SD IDOK<br /> Abort<br /> <br /> StrCpy $Shortcuts_SM_Checkbox_State 1<br /> StrCpy $Shortcuts_D_Checkbox_State 1<br />FunctionEnd<br /><br />Function un.onInit<br /> ; see Function .onInit<br /> FindWindow $0 "${MESSAGEWINDOW_NAME}"<br /> StrCmp $0 0 +3<br /> MessageBox MB_OK|MB_ICONEXCLAMATION "${PRODUCT_NAME} is running. Please close it first" /SD IDOK<br /> Abort<br />FunctionEnd<br /><br />; custom page creation, for the shortcuts installation, using nsDialog<br />Function onShortcutsPageCreate<br /> !insertmacro MUI_HEADER_TEXT $(l10n_SHORTCUTS_PAGE_TITLE) \<br /> $(l10n_SHORTCUTS_PAGE_SUBTITLE)<br /> <br /> nsDialogs::Create 1018<br /> Pop $Shortcuts_Dialog<br /> <br /> ${If} $Shortcuts_Dialog == error<br /> Abort<br /> ${EndIf}<br /><br /> ${NSD_CreateLabel} 0 6 100% 12u $(l10n_CREATE_ICONS_DESC)<br /> Pop $Shortcuts_Label<br /><br /> ${NSD_CreateCheckbox} 15u 20u 100% 10u $(l10n_ICONS_STARTMENU)<br /> Pop $Shortcuts_SM_Checkbox<br /> GetFunctionAddress $0 OnSMCheckbox<br /> nsDialogs::OnClick $Shortcuts_SM_Checkbox $0<br /> <br /> ${If} $Shortcuts_SM_Checkbox_State == ${BST_CHECKED}<br /> ${NSD_Check} $Shortcuts_SM_Checkbox<br /> ${EndIf}<br /><br /> ${NSD_CreateCheckbox} 15u 40u 100% 10u $(l10n_ICONS_DESKTOP)<br /> Pop $Shortcuts_D_Checkbox<br /> GetFunctionAddress $0 OnDCheckbox<br /> nsDialogs::OnClick $Shortcuts_D_Checkbox $0<br /><br /> ${If} $Shortcuts_D_Checkbox_State == ${BST_CHECKED}<br /> ${NSD_Check} $Shortcuts_D_Checkbox<br /> ${EndIf}<br /><br /> nsDialogs::Show<br />FunctionEnd<br /><br />; event when the Start Menu shortcut is (un)checked in the custom page<br />Function OnSMCheckbox<br /> ${NSD_GetState} $Shortcuts_SM_Checkbox $Shortcuts_SM_Checkbox_State<br /> Pop $0 # HWND<br />FunctionEnd<br /><br />; event when the Desktop shortcut is (un)checked in the custom page<br />Function OnDCheckbox<br /> ${NSD_GetState} $Shortcuts_D_Checkbox $Shortcuts_D_Checkbox_State<br /> Pop $0 # HWND<br />FunctionEnd<br /><br />Function WriteUninstallReg<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayName" \<br /> "${PRODUCT_NAME} (${PRODUCT_VERSION})"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "UninstallString" \<br /> "$INSTDIR\uninstall.exe"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "QuietUninstallString" \<br /> "$INSTDIR\uninstall.exe /S"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "InstallLocation" \<br /> "$INSTDIR"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayIcon" \<br /> "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /> WriteRegStr ${HKEY_ROOT} ${UN_KEY} "DisplayVersion" \<br /> "${PRODUCT_VERSION}"<br /> <br /> ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2<br /> IntFmt $0 "0x%08X" $0<br /> WriteRegDWORD ${HKEY_ROOT} ${UN_KEY} "EstimatedSize" "$0"<br />FunctionEnd<br /><br />; The stuff to install<br />Section ""<br /> ; uninstall an eventual previous installation<br /> ReadRegStr $Previous_Uninstall ${HKEY_ROOT} ${UN_KEY} "UninstallString"<br /> ClearErrors<br /> ${If} $Previous_Uninstall != ""<br /> StrCpy $Previous_Uninstall_dir $Previous_Uninstall<br /> ${GetParent} $Previous_Uninstall $Previous_Uninstall_dir<br /> <br /> IfFileExists "$Previous_Uninstall" myUninstallPrevious myInstall<br /> ${Else}<br /> goto myInstall<br /> ${EndIf}<br /> <br /> myUninstallPrevious:<br /> ; copy the previous uninstaller into TEMP<br /> ClearErrors<br /> StrCpy $TempUninstallPath "$TEMP\${TMP_UNINSTALL_EXE}"<br /> CopyFiles /SILENT "$Previous_Uninstall" "$TempUninstallPath"<br /> IfErrors myInstall<br /> <br /> ClearErrors<br /> ExecWait '"$TempUninstallPath" /S _?=$Previous_Uninstall_dir'<br /> <br /> ClearErrors<br /> Delete "$TempUninstallPath"<br /> <br /> ;MessageBox MB_OK "UNINSTALL: finished"<br /> <br /> myInstall:<br /> SetOutPath $INSTDIR<br /> <br /> ; copy the files<br /> File /r ${PRODUCT_INTERNAL_NAME}\*<br /> <br /> WriteUninstaller "uninstall.exe"<br /> <br /> Call WriteUninstallReg<br /> <br /> ${If} $Shortcuts_SM_Checkbox_State == ${BST_CHECKED}<br /> CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" \<br /> "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /> ${EndIf}<br /> <br /> ${If} $Shortcuts_D_Checkbox_State == ${BST_CHECKED}<br /> CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" \<br /> "$INSTDIR\${PRODUCT_INTERNAL_NAME}.exe"<br /> ${EndIf}<br />SectionEnd<br /><br />;--------------------------------<br />; Uninstaller<br /><br />Section "Uninstall"<br /> MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR" /SD IDOK<br /> ; Remove installed files and uninstaller<br /> !include ./uninstall_files.nsi<br /> Delete "$INSTDIR\uninstall.exe"<br /> <br /> ; remove installed directories<br /> !include ./uninstall_dirs.nsi<br /> RMDir /r "$INSTDIR\extensions"<br /> <br /> ; Remove shortcuts, if any<br /> Delete "$SMPROGRAMS\${PRODUCT_NAME}.lnk"<br /> Delete "$DESKTOP\${PRODUCT_NAME}.lnk"<br /> ;TODO remove eventual quicklaunch Too<br /> <br /> ; Remove the installation directory used (if empty)<br /> RMDir "$INSTDIR"<br /> <br /> ; and delete the registry key for uninstall<br /> DeleteRegKey ${HKEY_ROOT} ${UN_KEY}<br />SectionEnd</pre>
<p>Some descriptions about this script:</p>
<p>It uses the default "modern" skin. But it's possible to customize it.</p>
<p>
It uses some localized parts, including some other nsis scripts (described later).
</p>
<p>
It uses some default nsis pages, and a custom one to allow the creation of
shortcuts on the desktop and in the start menu.
</p>
<p>
The installation and uninstallation are aborted if our application is currently running.
<br />
In fact, when our app is running, XULRunner/Firefox create a native Windows window with
a class <code>MyAppMessageWindow</code>, this class name is the value of the field
<code>Name</code> of the <code>application.ini</code> concatenated with "MessageWindow".
<br />
The script just checks if such a window with this class name is opened, then abort.
</p>
<p>
It creates some minimal Registry entries to allow the uninstallation of the app
with the "add/remove programs" tool.
</p>
<p>
If our app is already installed, the previous version is uninstalled before the new
installation, using our previous uninstaller. And, to be more precise,
this previous uninstaller is copied and launched from the Windows temp folder.
</p>
<p>
For the uninstaller, we need the complete list of installed files and directories.
These lists will be dynamically created later, for the moment in this script we
just include them as other nsis scripts (<code>uninstall_files.nsi</code>
and <code>uninstall_dirs.nsi</code>).
</p>
<p>
Now here the content of one of the localized included script, en_US.nsh:
</p>
<pre>;!!! IMPORTANT: this must file must be edited with ANSI charset (in fact the<br />; Windows CP-1252 charset), but if there's no special character, UTF-8 is ok,<br />; because it's a subset ;)<br /><br />LangString l10n_SHORTCUTS_PAGE_TITLE ${LANG_ENGLISH} \<br /> "Set Up Shortcuts"<br />LangString l10n_SHORTCUTS_PAGE_SUBTITLE ${LANG_ENGLISH} \<br /> "Create Program Icons"<br />LangString l10n_CREATE_ICONS_DESC ${LANG_ENGLISH} \<br /> "Create icons for ${PRODUCT_NAME}:"<br />LangString l10n_ICONS_DESKTOP ${LANG_ENGLISH} \<br /> "On my &Desktop"<br />LangString l10n_ICONS_STARTMENU ${LANG_ENGLISH} \<br /> "In my &Start Menu Programs folder"</pre>
<p>
We have to be careful with the encoding of these files, this English
one is not a problem, but the French one must be edited with the Windows
<code>CP-1252</code> charset for exemple.
These variables are simply used in our main script with, for example,
<code>$(l10n_SHORTCUTS_PAGE_TITLE)</code>.
</p>
<p>
Note that the ampersand <code>&</code> in the string definitions
define an access key for controls in the UI, here there's <code>alt+D</code>
and <code>alt+S</code>.
</p>
</div>
<div class="howto-section">
<h4><span>3.4 Create the installer</span></h4>
<p>
We have now all the files to create the installer.
Let see our sources tree:
</p>
<pre> |-samples_chapter_3<br /> |- data/<br /> |- icons/<br /> |- icon.ico<br /> |- myapp.bat<br /> |- myapp-res.rc<br /> |- myapp/<br /> |- win<br /> |- nsis/<br /> |- l10n/<br /> |- en_US.nsh<br /> |- fr.nsh<br /> |- myapp.nsi<br /> |- xal-src/<br /> |- build.sh<br /> |- clean.sh<br /> |- main.c</pre>
<p>
We have to build our launcher from the C source and our data (icon and resource).
And create 2 additional nsis scripts, listing our app files.
Then we can create the installer with <code>makensis</code>. We will doing
this in a temporary folder. Here's the script, named <code>build_win.sh</code>:
</p>
<pre>#!/bin/bash<br /><br /># exit the script on errors<br />set -e<br /><br />TMP_DIR=./tmp<br /><br /># get the absolute path of our TMP_DIR folder<br />TMP_DIR=$(readlink -f -- "$TMP_DIR")<br /><br /># re-create the TMP_DIR folder<br />rm -rfv "$TMP_DIR"<br />mkdir -v "$TMP_DIR"<br /><br /># copy our app<br />cp -rv myapp "$TMP_DIR/"<br /><br /># copy the XAL launcher sources<br />cp -rv win/xal-src "$TMP_DIR/xal"<br /># and our icon and resource file<br />cp -v data/icons/icon.ico "$TMP_DIR/xal/myapp.ico"<br />cp -v data/myapp-res.rc "$TMP_DIR/xal/"<br /><br /># build the launcher<br />cd "$TMP_DIR/xal/"<br />sh build.sh myapp myapp-res.rc<br />cd -<br /><br /># copy the launchers in the right folder<br />cp -v data/myapp.bat "$TMP_DIR/myapp/"<br />cp -v "$TMP_DIR/xal/myapp.exe" "$TMP_DIR/myapp/"<br /><br /># delete the xal sources<br />rm -rv "$TMP_DIR/xal"<br /><br /># create the nsis script listing the files to unsinstall<br />cd "$TMP_DIR/myapp"<br />find . -maxdepth 1 -type f > ../uninstall_files.nsi<br />sed -i -r "s|^\./(.*)$| Delete \"\$INSTDIR\\\\\1\"|" ../uninstall_files.nsi<br /><br /># and the list of directory<br />ls -d */ > ../uninstall_dirs.nsi<br />sed -i -r "s|^(.*)/$| RMDir /r \"\$INSTDIR\\\\\1\"|" ../uninstall_dirs.nsi<br />cd -<br /><br /># copy the other nsis scripts<br />cp -rv win/nsis/* "$TMP_DIR/"<br /><br /># and create the installer<br />makensis "$TMP_DIR/myapp.nsi"<br /><br /># finally, copy our installer in the root sources dir<br />cp -v "$TMP_DIR/MyApp-1.0-install.exe" ./<br /><br />echo "Windows installer for myapp created."</pre>
<p>To launch this script, in a terminal:</p>
<ul class="code-xterm">
<li><pre>cd samples_chapter_3</pre></li>
<li><pre>sh ./build_win.sh</pre></li>
</ul>
<p>
And as result we have finally the file <code>MyApp-1.0-install.exe</code>
in the <code>samples_chapter_3</code> folder :) .
</p>
</div>
<div class="howto-navigator-bottom">
<div class="howto-previous"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/24/Distribute-your-XULRunner-app-24-Package-as-rpm" title="Go to chapter 2.4">< previous chapter</a></div>
<div class="howto-next"><a href="http://joliclic.free.fr/blog/index.php?post/2011/05/26/Distribute-your-XULRunner-app-4-Mac-OSX" title="Go to chapter 4">next chapter ></a></div>
<div class="clearer"> </div>
</div>
<div class="howto-author">
<p>Nicolas Martin</p>
</div>
<div class="howto-download-section">
<p>
You can download all the samples of this chapter 3 (Windows) in the
<a href="http://joliclic.free.fr/mozilla/howto/dist-xul-app/dl.php?id=current_sample_3" class="howto-download-archive">samples_chapter_3.tar.gz</a>
archive.
</p>
</div>
<div class="howto-credits">
<p><em>
The myapp application, from
<a href="https://developer.mozilla.org/en/getting_started_with_xulrunner" rel="external">developer.mozilla.org</a>
is in the <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a>.
</em></p>
<p><em>
The icon used is from the
<a href="http://tango.freedesktop.org" rel="external">Tango Desktop Project</a>,
and is in the <a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a>.
</em></p>
<p><em>
The C launcher
<a href="http://joliclic.free.fr/mozilla/xal/en/" rel="external">XUL App Launcher</a> (XAL)
is under the <a href="http://www.opensource.org/licenses/mit-license.php" rel="external">MIT license</a>.
</em></p>
<p><em>
All other added data, and sample files, of this chapter 3, are in the
<a href="http://wiki.creativecommons.org/Public_domain" rel="external">Public Domain</a> too.
</em></p>
</div>
<div class="howto-footer">
<p>
The content of this article
is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>
</p>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width: 0;" src="http://i.creativecommons.org/l/by-sa/3.0/80x15.png" /></a>
</div>
</div>http://joliclic.free.fr/blog/index.php?post/2011/05/25/Distribute-your-XULRunner-app-3-Windows#comment-formhttp://joliclic.free.fr/blog/index.php?feed/atom/comments/81