5. Créer une distribution compilée
**********************************

Une "distribution compilée" vous fait surement penser à un "paquet
binaire" ou à un "installateur" (tout dépend de votre environnement).
Ce n'est cependant pas forcément un binaire, il peut ne contenir que
des sources Python et / ou du *byte-code* ; et nous n'appelons pas ça
un *package* parce que ce mot est déjà utilisé dans Python (et
"installateur" est un terme spécifique au monde des systèmes de
bureau).

Une distribution compilée est une manière de rendre la vie plus facile
à ceux qui installent votre module : pour les utilisateurs de système
Linux basé sur RPM, ce sera des binaires RPM ; pour les utilisateurs
de Windows, c'est un installateur exécutable ; pour les utilisateurs
de Linux basés sur Debian, c'est un paquet Debian ; et ainsi de suite.
Évidemment, personne n'est capable de créer une distribution compilée
pour toutes les plateformes existantes, donc Distutils est fait pour
permettre aux développeurs de se concentrer sur leurs spécialités ---
écrire du code et créer des distributions source --- pendant que
d'autres, appelés *packagers* s'occupent de transformer les sources en
paquets pour chaque plateforme.

Bien sur, le développeur du module peut être son propre *packager* ;
ou le *packager* peut être un volontaire "quelque part" qui a accès à
une plateforme que le développeur initial n'a pas ; ou bien un
logiciel qui prend les sources périodiquement et les transforme en une
distribution compilée pour autant de plateforme que le logiciel a
accès. Peu importe qui ils sont, les *packagers* utilisent "setup.py"
et la famille de commandes **bdist** afin de générer une distribution
compilée.

Pour prendre un exemple simple, si je lance la commande suivante dans
les sources de Distutils :

   python setup.py bdist

alors Distutils construit ma distribution du module (Distutils lui-
même dans ce cas), fait une "fausse" installation (aussi dans le
dossier "build") et crée le type de distribution compilée par défaut
pour ma plateforme. Le format par défaut est un "bête" fichier *tar*
pour Unix et un simple installateur exécutable pour Windows (ce
fichier *tar* est considéré comme "bête" car il doit être décompressé
à un endroit précis pour fonctionner).

Par conséquent, la commande si dessus crée le fichier
"Distutils-1.0.*plat*.tar.gz" sur les systèmes Unix ; décompresser
cette archive *tar* au bon endroit installe Distutils exactement comme
si vous aviez téléchargé la distribution source et lancé "python
setup.py install" (le "bon endroit" est soit la racine du système de
fichier, ou le dossier de Python "*prefix*", tout dépend des options
données à la commande **bdist_dumb** ; la valeur par défaut est de
créer une distribution "bête" relative à "*prefix*").

Évidemment, pour une distribution en Python pur, ce n'est pas aussi
simple que de simplement lancer "python setup.py install"---mais pour
une distribution non-pure qui inclut des extensions qui devraient être
compilées, ça peut faire la différence entre quelqu'un qui sera
capable d'utiliser votre extension, ou non. De plus créer une
distribution compilée "intelligente" tel qu'un paquet RPM ou un
installateur exécutable Windows, est bien plus pratique pour les
utilisateurs, même si votre module n'inclut aucune extension.

La commande **bdist** a l'option "--formats", similaire à la commande
**sdist**, que vous pouvez utiliser pour sélectionner le type de
distribution compilée à générer : par exemple, :

   python setup.py bdist --format=zip

serait, lors d'une exécution sur un système Unix, crée
"Distutils-1.0.*plat*.zip"---à nouveau, cette archive devra être
décompressé depuis la racine pour installer Distutils.

Les formats disponibles pour les distributions compilées sont :

+---------------+--------------------------------+-----------+
| Format        | Description                    | Notes     |
|===============|================================|===========|
| "gztar"       | fichier *tar* *gzippé*         | (1)       |
|               | (".tar.gz")                    |           |
+---------------+--------------------------------+-----------+
| "bztar"       | fichier *tar* de type          |           |
|               | *bzipped* (".tar.bz2")         |           |
+---------------+--------------------------------+-----------+
| "xztar"       | fichier *tar* de type          |           |
|               | *xzipped* (".tar.xz")          |           |
+---------------+--------------------------------+-----------+
| "ztar"        | fichier *tar* compressé par    | (3)       |
|               | *compress* (".tar.Z")          |           |
+---------------+--------------------------------+-----------+
| "tar"         | fichier *tar* (".tar")         |           |
+---------------+--------------------------------+-----------+
| "zip"         | fichier *zip* (".zip")         | (2),(4)   |
+---------------+--------------------------------+-----------+
| "rpm"         | RPM                            | (5)       |
+---------------+--------------------------------+-----------+
| "pkgtool"     | Solaris **pkgtool**            |           |
+---------------+--------------------------------+-----------+
| "sdux"        | HP-UX **swinstall**            |           |
+---------------+--------------------------------+-----------+
| "wininst"     | fichier zip auto-extracteur    | (4)       |
|               | Windows                        |           |
+---------------+--------------------------------+-----------+
| "msi"         | Installateur Microsoft.        |           |
+---------------+--------------------------------+-----------+

Modifié dans la version 3.5: Ajout du support des fichiers "xztar".

Notes :

1. par défaut sur Unix

2. par défaut sur Windows

3. nécessite un programme externe **compress**.

4. nécessite soit un utilitaire **zip** extérieur ou le module
   "zipfile" (inclut dans la bibliothèque standard depuis Python 1.6)

5. nécessite un programme externe **rpm**, version 3.0.4 ou mieux
   (utilisez "rpm --version" pour connaître quelle version vous avez)

Vous ne devez pas utiliser la commande **bdist** avec l'option "--
formats" ; Vous pouvez également utiliser la commande qui implémente
directement le format qui vous intéresse. Certaines de ces "sous-
commandes" **bdist** génèrent plusieurs formats similaires ; par
exemple, La commande **bdist_dumb** génère les archives aux formats
"bêtes" ("tar", "gztar", "bztar", "xztar", "ztar", et "zip"), et
**bdist_rpm** génère les binaires RPM et ainsi que les sources. Les
sous-commandes **bdist**, et les formats générés par chacun, sont :

+----------------------------+---------------------------------------+
| Commande                   | Formats                               |
|============================|=======================================|
| **bdist_dumb**             | tar, gztar, bztar, xztar, ztar, zip   |
+----------------------------+---------------------------------------+
| **bdist_rpm**              | rpm, srpm                             |
+----------------------------+---------------------------------------+
| **bdist_wininst**          | wininst                               |
+----------------------------+---------------------------------------+
| **bdist_msi**              | msi                                   |
+----------------------------+---------------------------------------+

Le chapitre suivant donne des détails individuel sur les commandes
**bdist_***.


5.1. Créer un paquet RPM
========================

Le format RPM est utilisé par beaucoup de distributions Linux
populaires, incluant Red Hat, SuSE et Mandrake. Si l'une d'entre elle
(ou n'importe quelle autre distribution basé sur RPM) est votre
environnement habituel, créer un paquet pour les autres utilisateur de
ces distributions est trivial. Cela dépend de la complexité de votre
module et des différences entre les distributions Linux, vous pourrez
aussi créer des RPM qui fonctionneront sur des distributions RPM
différentes.

La manière habituelle de créer un RPM de votre module est d'utiliser
la commande **bdist_rpm** :

   python setup.py bdist_rpm

ou la commande **bdist**  avec l'option "--format" :

   python setup.py bdist --formats=rpm

La première vous permet de spécifier des options spécifiques à RPM ;
la dernière vous permet de spécifier plusieurs formats d'un seul coup.
Si vous avez besoin d'utiliser les deux, vous pouvez explicitement
spécifier plusieurs commandes **bdist_*** et leurs options :

   python setup.py bdist_rpm --packager="John Doe <jdoe@example.org>" \
                   bdist_wininst --target-version="2.0"

La création de Package RPM est configurée par un fichier ".spec", un
peu comme Distutils est configuré par script *setup.py*. Pour vous
faciliter la tâche, la commande **bdist_rpm** crée normalement un
fichier ".spec" basé sur les informations que vous fournissez dans
*setup.py*,  dans les options de la ligne de commande et dans la
configuration de Distutils. Beaucoup d'options du fichier ".spec" sont
dérivées des options du script de préparation suivant :

+--------------------------------------------+------------------------------------------------+
| Option de fichier RPM ".spec"              | Options du script de préparation Distutils     |
|============================================|================================================|
| Nom                                        | "name"                                         |
+--------------------------------------------+------------------------------------------------+
| *Summary* (dans le préambule)              | "description"                                  |
+--------------------------------------------+------------------------------------------------+
| Version                                    | "version"                                      |
+--------------------------------------------+------------------------------------------------+
| Vendor                                     | "author" et "author_email", ou  --- &          |
|                                            | "maintainer" et "maintainer_email"             |
+--------------------------------------------+------------------------------------------------+
| Copyright                                  | "license"                                      |
+--------------------------------------------+------------------------------------------------+
| Url                                        | "url"                                          |
+--------------------------------------------+------------------------------------------------+
| %description (section)                     | "long_description"                             |
+--------------------------------------------+------------------------------------------------+

De plus, il y a beaucoup d'options dans le fichier ".spec" qui n'ont
aucun équivalent dans le script de préparation. La plupart de celles
ci sont gérées par les options de la commande **bdist_rpm** suivant :

+---------------------------------+-------------------------------+---------------------------+
| Option de fichier RPM ".spec"   | Option de **bdist_rpm**       | valeur par défaut         |
|=================================|===============================|===========================|
| Version                         | "release"                     | "1"                       |
+---------------------------------+-------------------------------+---------------------------+
| Group                           | "group"                       | "Développement/Bibliothè  |
|                                 |                               | ques"                     |
+---------------------------------+-------------------------------+---------------------------+
| Vendor                          | "vendor"                      | (voir au-dessus)          |
+---------------------------------+-------------------------------+---------------------------+
| Packager                        | "packager"                    | (none)                    |
+---------------------------------+-------------------------------+---------------------------+
| Provides                        | "provides"                    | (none)                    |
+---------------------------------+-------------------------------+---------------------------+
| Requires                        | "requires"                    | (none)                    |
+---------------------------------+-------------------------------+---------------------------+
| Conflicts                       | "conflicts"                   | (none)                    |
+---------------------------------+-------------------------------+---------------------------+
| Obsoletes                       | "obsoletes"                   | (none)                    |
+---------------------------------+-------------------------------+---------------------------+
| Distribution                    | "distribution_name"           | (none)                    |
+---------------------------------+-------------------------------+---------------------------+
| BuildRequires                   | "build_requires"              | (none)                    |
+---------------------------------+-------------------------------+---------------------------+
| Icon                            | "icon"                        | (none)                    |
+---------------------------------+-------------------------------+---------------------------+

Fournir ces options en ligne de commande est fastidieux et sujet à des
erreurs, il est donc recommandé de les mettre dans un fichier de
configuration. "setup.cfg"--- voir la section Writing the Setup
Configuration File. Si vous distribuez ou empaquetez beaucoup de
modules de distribution Python, vous pourriez vouloir mettre les
options qui s'appliquent à tous vos projets dans votre fichier de
configuration personnel Distutils ("~/.pydistutils.cfg"). Si vous
voulez temporairement désactiver ce fichier, vous pouvez passer
l'option "--no-user-cfg" à "setup.py".

La création de binaire RPM se fait en trois étapes, chacune gérée
automatiquement par Distutils :

1. crée un fichier ".spec" qui décrit le paquet (comme le script de
   préparation Distutils, en réalité la plupart des informations du
   script de préparation se retrouve dans le fichier ".spec")

2. crée un RPM source

3. crée le RPM "binaire" (qui peut ou non contenir des binaires, tout
   dépend de si votre module contiens des extensions)

Normalement, RPM réunit les deux dernières étapes ensemble ; quand
vous utilisez Distutils, les trois étapes sont regroupées.

Si vous le souhaitez, vous pouvez séparer ces trois étapes. Vous
pouvez utiliser l'option "--spec-only" pour faire en sorte que la
commande **bdist_rpm** crée juste le fichier ".spec" et quitte ; dans
ce cas, le fichier ".spec" sera écrit dans le dossier de distribution
("*distribution directory*") --- normalement "dist/", mais
personnalisable avec l'option "--dist-dir".  (Normalement, le fichier
".spec" se retrouve profondément enfoui dans l'arborescence, dans un
dossier temporaire créé par la commande **bdist_rpm**.)


5.2. Créer un installateur Windows
==================================

Les installateurs exécutables sont le format naturel pour les
distributions sur Windows. Ils affichent une jolie interface
graphique, montrent quelques informations à propos du module qui va
être installé, tiré des métadonnées dans le script de préparation,
laisse l'utilisateur choisir quelques options et démarrer ou annuler
l'installation.

Étant donné que les métadonnées sont tirées du script de préparation,
créer un installateur Windows est généralement facile, il suffit de
lancer :

   python setup.py bdist_wininst

ou la commande **bdist** avec l'option "--formats" :

   python setup.py bdist --formats=wininst

If you have a pure module distribution (only containing pure Python
modules and packages), the resulting installer will be version
independent and have a name like "foo-1.0.win32.exe". Note that
creating "wininst" binary distributions in only supported on Windows
systems.

Si vous avez une distribution "non-pure", l'extension peut être créée
uniquement pour les plateformes Windows et sera lié à une version de
Python. Le nom de l'installateur reflétera ça et sera de format
"foo-1.0.win32-py2.0.exe". Vous devrez créer un installateur pour
chaque version de Python que vous voulez supporter.

L'installeur essaiera de compiler les modules purs en *bytecode* après
installation sur le système cible dans les modes normaux et optimisé.
Si vous ne voulez pas que ce soit fait, vous pouvez lancer la commande
**bdist_wininst** avec l'option "--no-target-compile" et/ou l'option "
--no-target-optimize".

Par défaut l'installateur affichera le super logo "Python Powered",
mais vous pouvez aussi donnez votre propre bitmap de 152x261 qui doit
être un fichier ".bmp" Windows avec l'option "--bitmap".

L'installateur affichera aussi un grand titre en arrière plan de la
fenêtre quand exécuté, qui est composé du nom de votre distribution et
du numéro de version. Cela peut être changé avec un autre texte avec
l'option "--title".

Le fichier d'installation sera écrit dans le dossier de distribution
("*distribution directory*") --- normalement "dist/", mais
personnalisable avec l'option "--dist-dir".


5.3. Compiler pour toute les plateformes Windows
================================================

Depuis Python 2.6, *distutils* est capable de compiler pour toutes les
plateformes de Windows. En pratique, cela signifie qu'avec les bons
outils installés, vous pouvez utiliser une version 32 bits de Windows
pour créer une extension 64 bits et vice-versa.

Pour construire pour une plateforme alternative, spécifiez l'option "
--plat-name" à la commande de **build**. Les valeurs valides sont
actuellement **'win32'**, et  **'win-amd64'**. Par exemple, sur une
version *32bit* de Windows, vous pourriez exécuter :

   python setup.py build --plat-name=win-amd64

pour construire une version 64 bits de votre module. L'installateur
Windows supporte aussi cette option, donc la commande :

   python setup.py build --plat-name=win-amd64 bdist_wininst

créera un installateur exécutable 64 bits depuis votre version 32 bits
de Windows.

Pour effectuer une compilation croisée (*cross-compile* en anglais),
vous devez télécharger le code source de Python et le compiler pour la
plateforme cible, il est impossible de le faire depuis une
installation de Python (vu que les fichiers *.lib* pour les autres
plateformes ne sont pas inclus). En pratique, cela veut dire qu'un
utilisateur de système 32 bits devra utiliser Visual Studio 2008 pour
ouvrir la solution "PCBuild/PCbuild.sln" dans l'arborescence des
sources de Python et construire la version *x64* du projet
*pythoncore* avant de pouvoir compiler son extension.

Remarquez que par défaut Visual Studio 2008 n'installe pas les outils
et compilateur 64 bits. Vous devrez peut-être ré-exécuter le processus
d'installation et sélectionner ces outils (utiliser le *Panneau de
Contrôle -> [Ajouter/Supprimer]* est un bon moyen de vérifier ou
modifier votre installation existante.)


5.3.1. Le script de post-installation
-------------------------------------

Depuis Python 2.3, un script post-installation peut être spécifié avec
l'option "--install-script". Le nom du script doit être spécifié et
présent dans l'argument "scripts" de la fonction "setup".

Ce script doit être lancé au moment de l'installation sur le système
cible après que tous les fichiers ont été copiés, avec la valeur de
"argv[1]" à  "-install", et une autre fois durant la désinstallation
avant que les fichiers soient supprimés avec la valeur de "argv[1]" à
"-remove".

Le script d'installation s'exécute intégré à la fenêtre
d'installation, chaque sortie ("sys.stdout", "sys.stderr") est
redirigé dans le tampon et sera affiché dans le GUI après que les
scripts soient finis.

Certaines fonctions spécialement utiles dans ce contexte sont
disponibles comme fonctions natives additionnelles dans le script
d'installation.

directory_created(path)
file_created(path)

   Ces fonctions peuvent être appelées lorsqu'un répertoire ou un
   fichier est crée par le script de post installation au moment de
   l'installation. Cela va enregistrer le *chemin* avec le des-
   installateur, de sorte qu'il soit retiré lors de la des-
   installation de la distribution. pour être sûr, les répertoires
   sont uniquement retirés s'ils sont vides.

get_special_folder_path(csidl_string)

   Cette fonction peut être utilisée pour extraire des localisations
   de fichiers spéciaux sous Windows comme un menu démarrer ou le
   Bureau. Cela renvoie le chemin complet pour le fichier.
   *csidl_string* doit être unes des chaines suivantes :

      "CSIDL_APPDATA"

      "CSIDL_COMMON_STARTMENU"
      "CSIDL_STARTMENU"

      "CSIDL_COMMON_DESKTOPDIRECTORY"
      "CSIDL_DESKTOPDIRECTORY"

      "CSIDL_COMMON_STARTUP"
      "CSIDL_STARTUP"

      "CSIDL_COMMON_PROGRAMS"
      "CSIDL_PROGRAMS"

      "CSIDL_FONTS"

   Si le fichier ne peut être extrait, "OSError" est obtenu..

   Quel dossier est disponible dépend de la version exacte de Windows,
   et probablement aussi de la configuration. pour des détails se
   référer à la documentation Microsoft de la fonction
   "SHGetSpecialFolderPath()".

create_shortcut(target, description, filename[, arguments[, workdir[, iconpath[, iconindex]]]])

   Cette fonction crée un raccourci. *cible* est le chemin au
   programme auquel mène le raccourci. *description* est la
   description du raccourci. *nomfichier* est le titre du raccourci
   que verra l'utilisateur. *arguments* spécifie les arguments de la
   ligne de commande, si existant. *cheminRep* est le répertoire de
   travail pour le programme. *cheminIcone* est le fichier contenant
   l’icône pour le raccourci, et *IndexIcone* est l'index pour l’icône
   dans le fichier *cheminIcone*. Encore, pour les détails consulter
   la documentation Microsoft pour l'interface "ILienShell".


5.4. Contrôle d'accès utilisateur Vista (UAC)
=============================================

Depuis Python 2.6, *bdist_wininst* accepte une option "--user-access-
control". Sa valeur par défaut est 'none' (Ce qui veut dire que aucun
UAC est fait), et les autres valeurs valides sont 'auto' (ce qui veut
dire qu'il faut demander une élévation de privilèges UAC si Python a
été installé pour tous les utilisateurs) et 'force' (Toujours demander
une élévation de privilèges).
