Monday, March 20, 2023

qmake (CMake and 'friends' pour une autre fois)

j'ai mis un tag 'tutoriel', mais considérez ceci comme juste un moyen pour moi de ne pas oublier les griffes que le chat m'a fait pendant que j'essayais d'apprendre à me servir d'un nouvel outil

Bon, des Makefiles, ça va faire plus de 20 ans que j'en mange. C'est plus ou moins la base des règles de compilation. Vous indiquez un nom de fichier à obtenir, puis les fichiers dont il dépend, puis les commandes à exécuter pour passer de l'un à l'autre.


Monfichier.o: Monfichier.c FichierImportant.h
	compiler Monfichier.c -o Monfichier.o
     
Et bien sûr, vous avez le droit de rendre ça plus générique:

%.o: %.c FichierImportant.h
	compiler $< -o $@

Bon, assez rapidement, ça devient plus complexe que ça, hein. Par exemple, il y a peu de chance que tout votre projet dépende de FichierImportant.h et seulement de ça. On risque plus d'avoir quelque-chose comme jeu.c qui dépend de jeu.h et sprite.h alors que sprite.c ne dépend que de sprite.h et menu.c dépend à la fois de menu.h sauvegardes.h et sprite.h, par exemple.

Si on a de la chance, le compilateur offre alors une option pour générer des fichiers de dépendances qui capturent ça (disons, jeu.dep, menu.dep et sprite.dep) et on s'en sort avec


%.o: %.c
	compiler $< -o $@ $(ET_GENERER_LES_DEPENDANCES)

-include *.dep

Mais restons-en là pour l'instant. Dès que le projet grossit, qu'il commence à dépendre de bibliothèque externes (pour les formats d'images, la gestion du son, etc.), et surtout, dès qu'on commence à vouloir le compiler pour plusieurs plate-formes différentes (linux et windows?), ça devient vite un beau gros sac de noeuds ... en particulier parce que si make est omniprésent dans le monde Linux, il est toujours boudé par Microsoft qui y va de son propre msbuild.exe qui manipule des fichiers XML déguisés en .vcxproj ... D'où l'intérêt de programmes qui vont générer des Makefiles à partir de descriptions plus haut-niveau du projet à compiler.

C'est le cas de qmake, notamment, qu'on va exécuter dans un répertoire vide en lui indiquant l'emplacement de nos sources et qui va créer un Makefile dédié à la compilation de nos sources dans ce répertoire-là en utilisant le fichier *.pro distribué avec les sources.


  mkdir build
  cd build
  qmake ../src
  
  • il y définit tous les programmes utilisés (compilateur, linkeur, etc.) dans des variables pour make;
  • il donne une règle indiquant quand re-générer le Makefile (est-ce que le fichier .pro a changé?) qui réappellera qmake;
  • il ajoute les règles pour préparer une distribution du programme, nettoyer les fichiers intermédiaires, etc.
  • et surtout, parce que qmake est lié au projet d'interface graphique Qt, il prend en charge tout ce qui concerne la génération de code pour l'interface graphique à l'aide de moc.

A travers son fichier .pro, qmake prend en charge le fait que les différents compilateurs ont des préférences différentes pour les macro-définitions (à ajouter dans DEFINES) ou les répertoires à explorer quand il tombe sur un #include (à ajouter dans INCLUDEPATH). Idem avec les bibliothèques à passer au linkeur. On trouve évidemment aussi SOURCES pour les fichiers à compiler et HEADERS pour les déclaration de classes contenant des annotations pour le système de signal/slot propre à Qt (qui en aura besoin pour générer du code et compléter les vtables).

Une autre grande différence entre make et qmake est que ce dernier se veut multi-plate-formes et indépendant du shell. ça se traduit notamment par une manière franchement confortable pour les éléments conditionnels, comme


linux {
    LIBS += pthread
    !isEmpty(ENABLE_GL) {
       SOURCES += backend/opengl.cpp
    }
}
windows {
    LIBS += direct3D
}

Et autres astuces du genre. Attention par contre: si il était possible de faire des Makefile qui incluent d'autre makefiles (notamment pour combiner les résultats de plusieurs répertoires), la chose qui s'en rapproche le plus avec qmake c'est le TEMPLATE = subdirs, mais qui à l'instar de makefiles récursifs, ne partage pas les variables d'un fichier .pro à l'autre. Oui, je sais, en lisant ça, ça semble évident, mais ça l'était nettement moins en écrivant le makefile. La solution, c'est de faire venir la variable de l'extérieur soit avec qmake ENABLE_GL=y sur la ligne de commande, soit avec Qt Creator


 Une approche pas toujours très confortable, cela dit, en particulier à cause d'une gestion des dépendances entre variables et fichiers .o pas toujours très claire ... je comprends pourquoi mes collègues lui ont préféré include (../projet.pri) dans les .pro-feuilles et qui contient les DEFINES += ENABLE_GL=yes et autre MODEL_EXPORTER_PATH=/usr/bin/export_to_gl

2 comments:

PypeBros said...

oh, et si, il y a des gens qui utilisent qmake pour nds

windows said...

QtCreator can reveal you which qmake prompt it uses for this or that configuration. You may find sub-directories Makefile.Debug and Makefile.Release, but there will be just one Makefile in your top-level build directory, and that's the one matching the configuration you were selecting in QtCreator.

oh, and don't look for "make.exe". It's called jom.exe. Note that it still support -p ;)