4. Construire des extensions C et C++ sur Windows

Cette page explique rapidement comment créer un module d’extension Windows pour Python en utilisant Microsoft Visual C++, et donne plus d’informations contextuelles sur son fonctionnement. Le texte explicatif est utile tant pour le développeur Windows qui apprend à construire des extensions Python que pour le développeur Unix souhaitant produire des logiciels pouvant être construits sur Unix et Windows.

Les auteurs de modules sont invités à utiliser l’approche distutils pour construire des modules d’extension, au lieu de celle décrite dans cette section. Vous aurez toujours besoin du compilateur C utilisé pour construire Python ; typiquement Microsoft Visual C++.

Note

Cette page mentionne plusieurs noms de fichiers comprenant un numéro de version Python encodé. Ces noms de fichiers sont construits sous le format de version XY ; en pratique, 'X' représente le numéro de version majeure et 'Y' représente le numéro de version mineure de la version Python avec laquelle vous travaillez. Par exemple, si vous utilisez Python 2.2.1, XY correspond à 22.

4.1. Une approche « recette de cuisine »

Il y a deux approches lorsque l’on construit des modules d’extension sur Windows, tout comme sur Unix : utiliser le paquet distutils pour contrôler le processus de construction, ou faire les choses manuellement. L’approche distutils fonctionne bien pour la plupart des extensions ; la documentation pour utiliser distutils pour construire et empaqueter les modules d’extension est disponible dans Distribuer des modules Python (Version historique). Si vous considérez que vous avez réellement besoin de faire les choses manuellement, il pourrait être enrichissant d’étudier le fichier de projet winsound pour le module de la bibliothèque standard.

4.2. Différences entre Unix et Windows

Unix et Windows utilisent des paradigmes complètement différents pour le chargement du code pendant l’exécution. Avant d’essayer de construire un module qui puisse être chargé dynamiquement, soyez conscient du mode de fonctionnement du système.

Sur Unix, un fichier objet partagé (.so) contient du code servant au programme, ainsi que les noms des fonctions et les données que l’on s’attend à trouver dans le programme. Quand le fichier est attaché au programme, toutes les références à ces fonctions et données dans le code du fichier sont modifiées pour pointer vers les localisations actuelles dans le programme où sont désormais placées les fonctions et données dans la mémoire. C’est tout simplement une opération de liaison.

Sur Windows, un fichier bibliothèque de liens dynamiques (.dll) n’a pas de références paresseuses. A la place, un accès aux fonctions ou données passe par une table de conversion. Cela est fait pour que le code DLL ne doive pas être réarrangé à l’exécution pour renvoyer à la mémoire du programme ; à la place, le code utilise déjà la table de conversion DLL, et cette table est modifiée à l’exécution pour pointer vers les fonctions et données.

Sur Unix, il n’y a qu’un type de bibliothèque de fichier (.a) qui contient du code venant de plusieurs fichiers objets (.o). Durant l’étape de liaison pour créer un fichier objet partagé (.so), le lieur peut informer qu’il ne sait pas où un identificateur est défini. Le lieur le cherchera dans les fichiers objet dans les bibliothèques ; s’il le trouve, il inclura tout le code provenant de ce fichier objet.

Sur Windows, il y a deux types de bibliothèques, une bibliothèque statique et une bibliothèque d’import (toutes deux appelées .lib). Une bibliothèque statique est comme un fichier Unix .a ; elle contient du code pouvant être inclus si nécessaire. Une bibliothèque d’import est uniquement utilisée pour rassurer le lieur qu’un certain identificateur est légal, et sera présent dans le programme quand la DLL est chargé. Comme ça le lieur utilise les informations provenant de la bibliothèque d’import pour construire la table de conversion pour utiliser les identificateurs qui ne sont pas inclus dans la DLL. Quand une application ou une DLL est lié, une bibliothèque d’import peut être générée, qui devra être utilisée pour tous les futures DLL dépendantes aux symboles provenant de l’application ou de la DLL.

Supposons que vous construisez deux modules de chargement dynamiques, B et C, qui ne devraient pas partager un autre bloc de code avec A. Sur Unix, vous ne transmettrez pas A.a au lieur pour B.so et C.so ; cela le ferait être inclus deux fois, pour que B et C aient chacun leur propre copie. Sur Windows, construire A.dll construira aussi A.lib. Vous transmettez A.lib au lieur pour B et C. A.lib ne contient pas de code ; il contient uniquement des informations qui seront utilisées lors de l’exécution pour accéder au code de A.

Sur Windows, utiliser une bibliothèque d’import est comme utiliser import spam; cela vous donne accès aux noms des spams, mais ne crée par de copie séparée. Sur Unix, se lier à une bibliothèque est plus comme from spam import * ; cela créé une copie séparée.

4.3. Utiliser les DLL en pratique

Le Python de Windows est construit en Microsoft Visual C++ ; utiliser d’autres compilateurs pourrait fonctionner, ou pas (cependant Borland a l’air de fonctionner). Le reste de cette section est spécifique à MSVC++.

Lorsque vous créez des DLL sur Windows, vous devez transmettre pythonXY.lib au lieur. Pour construire deux DLL, spam et ni (qui utilisent des fonctions C trouvées dans spam), vous pouvez utiliser ces commandes :

cl /LD /I/python/include spam.c ../libs/pythonXY.lib
cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib

La première commande a créé trois fichiers : spam.obj, spam.dll et spam.lib. Spam.dll ne contient pas de fonctions Python (telles que PyArg_ParseTuple()), mais il sait comment trouver le code Python grâce à pythonXY.lib.

La seconde commande a créé ni.dll (et .obj et .lib), qui sait comment trouver les fonctions nécessaires dans spam, ainsi qu’à partir de l’exécutable Python.

Chaque identificateur n’est pas exporté vers la table de conversion. Si vous voulez que tout autre module (y compris Python) soit capable de voir vos identificateurs, vous devez préciser _declspec(dllexport), comme dans void _declspec(dllexport) initspam(void) ou PyObject _declspec(dllexport) *NiGetSpamData(void).

Developer Studio apportera beaucoup de bibliothèques d’import dont vous n’avez pas vraiment besoin, augmentant d’environ 100ko votre exécutable. Pour s’en débarrasser, allez dans les Paramètres du Projet, onglet Lien, pour préciser ignorer les bibliothèques par défaut. Et la msvcrtxx.lib correcte à la liste des bibliothèques.