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é (.tar.gz) (1)
bztar fichier tar de type bzipped (.tar.bz2)  
xztar fichier tar de type xzipped (.tar.xz)  
ztar fichier tar compressé par compress (.tar.Z) (3)
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 Windows (4)
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

Si vous avez un module pur (contenant seulement des modules et des packages en Python pur), l'installateur final ne sera lié a aucune version de Python et aura un nom du type foo-1.0.win32.exe. Ces installateurs peuvent même être créés sur les plateformes Unix ou Mac OS X.

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

Starting with Python 2.3, a postinstallation script can be specified with the --install-script option. The basename of the script must be specified, and the script filename must also be listed in the scripts argument to the setup function.

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 intégrées 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).