I. L'article original

Qt Labs est un site géré par les développeurs de Qt. Ils y publient des projets, des idées propres et des composants afin d'obtenir les retours d'informations sur les API, le code et les fonctionnalités ou simplement pour partager avec nous ce qui les intéressent. Le code que vous y trouverez peut fonctionner tel quel mais c'est sans aucune garantie ni support.

Nokia, Qt, Qt Labs et leurs logos sont des marques déposées de Nokia Corporation en Finlande et/ou dans les autres pays. Les autres marques déposées sont détenues par leurs propriétaires respectifs.

Cet article est la traduction du billet To make, or not to make - QMake and beyond, par Marius.

Note du traducteur : dans le titre, make semble correspondre à l'outil make et non au verbe, d'où une troncature du titre.

L'article a eu de nombreux commentaires, méritant pour la quasi-totalité d'être étudiés et ce, au point où l'auteur a publié un second article pour y répondre de manière concrète. Voici une liste de liens vers les pages liées au sujet :

II. Introduction

Il y a eu une abondance de discussions internes durant des années concernant l'abandon de QMake et son renouvellement : choisir l'un des systèmes de compilation existants ou bien dépenser des ressources supplémentaires pour tenter d'arranger QMake (sans briser la compatibilité). Quelques projets débutèrent mais n'aboutirent jamais. Un projet (QBuild) a même été publié avec Qtopia, mais n'a pas été terminé et n'a pas été développé davantage.

Compte tenu des désaccords de l'ensemble des développeurs de Qt, les discussions furent ardentes et les avis furent aussi nombreux que l'étaient les développeurs. Il n'existe réellement pas d'outil pouvant satisfaire la totalité de notre liste de désirs, nous avons pourtant exploré beaucoup d'entre eux. Chaque langage est dépassé, manquant de propriétés, trop dur à modeler de manière à effectuer des trivialités (le XML, par exemple), le langage n'est pas assez limité (Python et toutes ses bibliothèques, par exemple), trop lent, ne parallélise pas bien, ne voit pas toutes les dépendances ou prend une éternité à les traiter. Cela bifurque et la liste s'allonge encore et encore.

Ainsi, dans le but de rendre la décision encore plus ardue, nous vous demandons votre avis sur la question.

Je vous présenterai des opinions concernant un nouveau système de compilation potentiel, et je vous présenterai comment les aborder.

III. Langage « propriétaire »

Comme nous l'avons déjà expérimenté avec QMake, le maintien d'un langage nous étant propre est quasi-optimal : on doit travailler activement sur de nouvelles fonctionnalités et sur le fonctionnement interne de QMake. On finira probablement la conception dans des mauvaises structures, ce qui entravera un nouveau développement et ne donnera pas autant de sens qu'éventuellement d'autres constructions de langage. Cela peut rendre le langage dur à apprendre pour les nouveaux utilisateurs. Un exemple peut être vu dans ce fichier :

 
Sélectionnez
QTDIR/mkspecs/features/exclusive_builds.prf

Un autre problème d'un langage propriétaire est qu'il crée de nouveaux obstacles à surmonter par les nouveaux utilisateurs avant de pouvoir utiliser le système, car ils ne peuvent pas faire le rapprochement avec leurs expériences avec d'autres langages.

En effet, cela serait une bonne chose que le langage de ce nouveau système soit un langage avec lequel la plupart des gens pourraient faire le rapprochement, par exemple JavaScript, qui devient de plus en plus répandu (http://www.langpop.com/). L'utilisation d'un langage déjà paramétré nous permettrait de nous focaliser entièrement sur la manière d'utiliser le langage de la façon la plus efficace et la plus propre, à la différence de créer aussi un langage.

Nous devrons sans doute aussi nous éloigner des langages de script tiers, arrivant avec une abondance de bibliothèques d'utilitaires, qui rendent très ardu le renforcement de la structure d'un système de compilation, tenant compte de trop de personnalisations. Les gens qui empruntent cette voie doivent probablement utiliser un script personnalisé pour leur développement.

IV. Intégration à l'EDI

Le système de compilation se doit de bien s'intégrer aux EDI.

Nous avons créé notre propre EDI et cet EDI doit interagir convenablement avec les opérations de compilation de Qt et des projets basés sur Qt. Cela signifie que l'EDI doit pouvoir afficher tous les fichiers pour toutes les différentes plateformes en même temps, et aussi savoir lesquels sont compilés sur les plateformes sources et cibles (compilation cross-plateformes mixte), pour garantir que l'IntelliSense/la complétion de code fonctionne comme prévu. Il doit aussi pouvoir altérer les opérations de compilation, comme par ajout/modification/suppression d'options et de fichiers du compilateur/éditeur de liens, ce qui doit alors se refléter dans le système de compilation des fichiers du projet.

Ce n'est pas aussi simple que la création d'un fichier séparé qui spécifie les sources nécessaires à une compilation, puisque nous avons besoin de lister les fichiers spécifiques aux plateformes, et aussi de changer les paramètres dépendant de la plateforme.

Le problème provient bien sûr des paramètres conditionnels du langage, puisqu'ils sont évalués au moment de l'analyse et sont normalement méconnus de l'EDI qui a besoin d'interpréter le langage du projet.

Une solution possible que nous avons trouvée en réponse à cette question serait de garantir que les paramètres soient visibles après l'analyse, et faciles à évaluer. En JavaScript, nous pourrions définir les cibles comme ceci :

 
Sélectionnez
    var corelib = new Object()
    corelib.target = "QtCore"
    corelib.defines                 = ["QT_BUILD_CORE_LIB",
                                       "QT_NO_USING_NAMESPACE"]
    corelib.defines["mac && bundle"]= ["QT_NO_DEBUG_PLUGIN_CHECK"];
    corelib.sources                 = ["Foo.cpp", "Bar.cpp"]
    corelib.sources["windows"]      = ["Foo_win.cpp", "Bar_win.cpp"]
    corelib.sources["unix && !mac"] = ["Foo_x11.cpp", "Bar_x11.cpp"]

Ou, encore plus performant pour un EDI, comme JSON :

 
Sélectionnez
    var corelib = {
        "target" : "QtCore",
        "defines" : { "all"           : ["QT_BUILD_CORE_LIB",
                                         "QT_NO_USING_NAMESPACE"],
                      "mac && bundle" : ["QT_NO_DEBUG_PLUGIN_CHECK"] },
        "sources" : { "all"           : ["Foo.cpp", "Bar.cpp"],
                      "windows"       : ["Foo_win.cpp", "Bar_win.cpp"],
                      "unix && !mac"  : ["Foo_x11.cpp", "Bar_x11.cpp"] }
        }

(Notez que « toute » entrée du tableau devrait équivaloir à un tableau normal dans le code non-JSON.)

Ainsi, l'EDI pourrait voir toutes les sources possibles, et savoir à quelle configuration elles appartiennent. Alors les back-ends évalueraient les sources finales basées sur la configuration actuelle, lors de l'ajout de la cible « corelib » au DAG (Directed Acyclic Graph), ou arbre de dépendances du projet si vous voulez.

V. Compiler directement à partir d'un outil

Comme l'outil lui-même possède l'arbre DAG complet, il devrait être capable de compiler toutes les cibles directement, sans passer par un autre système de compilation.

CMake, par exemple, s'appuie sur le générateur de Makefile et crée des Makefiles qui a leur tour sont rappelés dans CMake pour effectuer la compilation. Cette solution n'est pas seulement lourde ; elle est également limitée puisque vous ne pouvez pas paralléliser plus que ce que le générateur utilisé vous le permet. Ainsi, à moins que le Makefile de la sortie ne soit un énorme Makefile, vous serez limité.

Scons va compiler directement, mais est trop lent pour l'analyse des dépendances, donc chaque compilation que vous effectuerez prendra une éternité à démarrer. Waf est mieux dans ce sens, mais tous deux manquent d'un ensemble de back-ends pour les générations de projets (Vcproj, XCode, Makefiles, etc.).

Ils sont aussi basés sur Python, qui ajoute une dépendance à une bibliothèque tierce, ce que nous voulons éviter, à la fois à cause de la multitude des plateformes que Qt supporte, et en raison de l'utilité de toutes les bibliothèques.

VI. Intégration vers les systèmes de compilation distribuée

Nous utilisons en interne de multiples systèmes de compilation distribuée, dépendant de la plateforme sur laquelle nous sommes (Teambuilder pour Linux, Distributed Network Builds pour Mac et IncrediBuild pour Windows), et tous ont la même limitation lorsque nous utilisons les Makefiles : ils soulèvent plus d'opérations que nécessaire.

Disons que vous avez une ferme de compilation avec vingt slots ouverts. Vous déclenchez « make -j20 » dans l'intention de remplir la ferme. Cependant, au même moment, quelqu'un d'autre fait la même chose et vous obtenez uniquement dix slots chacun. Vous aurez alors dix processus « endormis », tous étant uniquement en attente de leur slot dans la ferme, alors que ces ressources seraient bien mieux employées ailleurs.

La compilation de back-end devrait pouvoir se relier avec les divers outils pour voir quelles sont les ressources disponibles à un moment donné. De cette façon, elle augmenterait ou diminuerait le nombre de travaux parallèles pour atteindre l'optimal. En outre, le système de compilation distribuée sait probablement ce qu'il y a de mieux, car cela pourrait ne pas être optimal de lancer 1 compilation locale et 20 à distance, tandis que votre machine est liée à un autre core ; peut-être que 0 compilation locale et 10 à distance serait mieux pour votre machine, même s'il y a 20 slots ouverts dans la ferme ?

L'outil de compilation ne devrait pas savoir cela et le développeur ne devrait pas avoir à penser comment il veut que sa compilation soit parallélisée. C'est pourquoi une telle interface est importante, de sorte qu'il soit possible pour les systèmes de compilation distribuée d'ajouter ces algorithmes à l'outil de compilation.

VII. Cross-compilation

Dans les grands projets cross-compilés de manière mixte (où vous compilez les projets pour les systèmes hôtes et cibles en même temps), la plupart des sous-projets compilent normalement pour le système cible, tandis que seulement une minorité de projets sont destinés à l'hôte. Cela signifie que les projets devraient facilement et automatiquement relever la plateforme cible, tandis qu'un peu plus d'attention devrait être requise pour les projets de plateforme hôte uniquement.

Les projets ne devraient ni avoir à trop se soucier de la plateforme, ni à se soucier de la méthode à suivre pour manipuler le fichier de projet pour aboutir au résultat. Idéalement, ils devraient uniquement noter si un projet est prévu pour la plateforme hôte, comme pour cet exemple :

 
Sélectionnez
    var moc = {
        "target" : "moc",
        "platform": "Host",
        "defines" : { "all"           : ["QT_MOC"]},
        ...
        }

Comme le processus de configuration a déjà déterminé quels paramètres sont nécessaires pour les plateformes cibles et hôtes, le système de compilation devrait savoir quelles particularités par défaut appliquer à chaque type de projets dans la préparation de la cross-compilation ; donc le développeur du projet n'aurait pas à faire quoi que ce soit d'autre au projet, à moins qu'il ait besoin d'outrepasser les particularités par défaut de la cross-compilation.

VIII. Support de pkg-config

Le système de compilation a besoin de pouvoir utiliser les informations de pkg-config et de produire les fichiers pkg-config pour le projet compilé. Les projets individuels ne devraient pas avoir besoin de maintenir un fichier .pc séparé avec les variables de remplacement.

IX. Déploiement

Le système de compilation devrait pouvoir créer des fichiers de déploiement, comme .msi, .rpm et .dmg, pour diverses plateformes connues. Bien sûr, il pourrait faire cela par la création de scripts qui seraient exécutés avec les outils spécifiques de la plateforme pour créer ces fichiers de déploiement.

X. Configuration

L'outil devrait gérer la configuration des projets. De cette manière, nous aurions uniquement besoin de maintenir un ensemble de règles de configuration sur toutes les plateformes, et l'extension avec de nouvelles options devrait être uniforme. Idéalement, le système devrait aussi automatiquement générer la documentation -h/-help, basée sur le script de configuration, de manière à ce que nous n'ayons pas besoin de maintenir un fichier séparé de documentation avec le script de configuration.

C'est pour cette même raison que nous gardons autant de documentation dans le code source de Qt : pour garder la maintenance de la documentation aussi facile et à jour que possible.

XI. Conclusion

Ainsi, voici quelques-unes des choses auxquelles nous avons pensé. Cependant, il y a bien plus pour un système de compilation que ces huit éléments. De plus, il est nécessaire de mentionner que rien n'est fixé pour le moment. Nous en sommes à une étape de réflexion. Peu importe ce que nous déciderons, QMake sera toujours présent, et ce pour une longue période.

Alors, quelle est votre opinion ?

XII. Divers

Merci à dourouc05 et à gbdivers pour leur relecture et leurs encouragements lors de la traduction !

Merci aussi à worm83 et à jacques_jean pour leur relecture.