2 points par GN⁺ 2024-11-30 | 1 commentaires | Partager sur WhatsApp
  • Publié en 2011, Ninja est parti d’un système de build proche de Make pour devenir un exemple emblématique de réussite open source, adopté par Chrome, certaines parties d’Android, Meson et des projets basés sur CMake
  • Ce qui a fait la différence entre succès et échec relevait moins du code lui-même que de l’architecture, et plus encore de problèmes sociaux comme la maintenance et les attentes des utilisateurs
  • Ninja lit les commandes, entrées et sorties décrites dans ninja.build, vérifie les dates de modification des fichiers, puis exécute en parallèle les tâches nécessaires, avec pour objectif de rendre rapides les builds incrémentaux de grands projets
  • Son choix de conception le plus important a été de ne pas intégrer directement de fonctionnalités de build de haut niveau et de rester un exécuteur de graphe d’actions (action graph), en laissant les décisions complexes au générateur (generator)
  • L’intégration à CMake, la prise en charge de Windows et l’exécution parallèle par défaut ont favorisé son adoption, mais les demandes et contributions en conflit avec ses objectifs de conception sont restées une lourde charge de maintenance open source

Le contexte qui a rendu Ninja largement utilisé

  • Ninja est un système de build proche de Make, publié il y a environ neuf ans ; au départ, c’était un projet parallèle partagé avec une certaine gêne, mais il a ensuite été largement adopté
  • Ses principaux cas d’usage sont les suivants
    • Chrome a fini par supprimer tous les builds non-Ninja
    • Android utilise Ninja pour certaines grandes composantes du système
    • Le projet Meson utilise Ninja et semble devenir un système de build de plus en plus courant dans le monde du logiciel libre
    • De nombreux projets utilisent aussi Ninja avec CMake, et les instructions de build de Swift demandent également d’installer Ninja
  • Ninja est sorti en 2011, la propriété du projet a été transférée en 2014, puis elle est passée ensuite à un troisième mainteneur principal
  • L’expérience centrale qui ressort de cette rétrospective est que l’architecture compte davantage que le code, et que les enjeux sociaux comptent davantage encore que l’architecture

Fonctionnement de base pour des builds rapides

  • Ce que fait Ninja est relativement simple
    • L’utilisateur écrit dans un fichier ninja.build les commandes à exécuter, les fichiers que chaque commande consomme et ceux qu’elle produit
    • Ninja lit ce fichier et vérifie les dates de modification de plusieurs fichiers
    • Il exécute en parallèle les commandes nécessaires pour remettre l’ensemble à jour
  • Par rapport à Make, le langage de build en entrée offre moins de fonctionnalités, et la structure se concentre sur l’exécution très rapide d’un petit nombre de fonctions
  • Le flux d’exécution se divise en trois étapes
    • Analyse et interprétation du fichier de build
    • Vérification des dates de modification des fichiers d’entrée
    • Exécution des commandes nécessaires
  • L’objectif est d’atteindre la troisième étape aussi vite que possible, même sur de grands projets comptant plus de 100 000 fichiers d’entrée
  • Exemple d’optimisation : Ninja associe les chemins de fichiers d’entrée à des objets mémoire uniques le plus tôt possible, puis utilise ensuite des comparaisons de pointeurs pour tester l’égalité des chemins
  • Les détails de performance de plus bas niveau sont abordés dans le chapitre sur Ninja de The Performance of Open Source Software
  • Plusieurs réimplémentations de Ninja sont aussi apparues au fil des années
    • llbuild et Shake prennent en charge les fichiers Ninja en entrée
    • samurai est un projet réimplémenté presque fichier par fichier ; son code est plus réduit, mais il offre aussi moins de fonctionnalités et n’a pas de tests
  • Réaliser les 20 % amusants est facile, mais les 80 % restants comportent beaucoup de détails, et aucune implémentation plus rapide n’est connue

Les choix qui ont compté dans la conception

  • Représentation du graphe

    • Make gère mal les cas où une règle de build produit plusieurs fichiers
    • Ninja utilise non pas un graphe entre fichiers, mais un graphe biparti entre fichiers et commandes
    • Les nœuds de fichiers deviennent des arêtes entrant dans des nœuds de commandes, et les nœuds de commandes ont à leur tour des arêtes sortant vers les fichiers produits
    • Cette structure capture mieux la forme réelle d’un build
    • Si l’une des entrées change, la commande devient obsolète ; lorsque la commande est exécutée, toutes ses sorties sont mises à jour
    • Un fichier donné ne peut avoir au maximum qu’une seule arête d’entrée
    • La ligne de commande elle-même peut aussi être considérée comme une entrée du nœud de commande ; si les flags de la ligne de commande changent, la commande et ses sorties deviennent obsolètes
  • deps log et dépendances des en-têtes C

    • Pour traiter correctement les dépendances des en-têtes C, il faut consommer les données de dépendance supplémentaires produites par le compilateur C
    • Après avoir hésité entre l’introduction d’une base de données et la simplicité, le format de représentation deps log a été créé
    • Ce format est assez compact, mais il reste encore incorrect sur certains points importants
  • Conception end-to-end / crash-only

    • Ninja n’est pas un processus démon qui s’exécute en continu ; à chaque exécution, il repart de zéro
    • Ce choix est une architecture influencée par le principe end-to-end et le logiciel crash-only
    • Si le chemin d’exécution depuis le début est rendu rapide, il n’est pas nécessaire de créer un chemin de code « en ligne » séparé
    • Les projets qui peuvent rester en mémoire ont tendance, au final, à négliger les performances de démarrage
  • Vérification de l’état des fichiers

    • L’une des raisons pour lesquelles on s’attend à ce qu’un outil de build reste en mémoire est la mise en cache de l’état des fichiers sur disque
    • En réalité, le noyau met déjà ces informations en cache en mémoire, et les remettre en cache côté espace utilisateur n’économise pas grand-chose
    • Sous Linux, récupérer l’état d’un fichier est très rapide, et Ninja le fait sur un seul thread
    • Même sur une machine considérée comme « rapide » il y a dix ans, il était possible d’exécuter stat sur 30 000 fichiers en quelques dizaines de millisecondes
  • Compromis entre échelle et spécification

    • Selon une règle empirique, passer à une échelle 2 fois supérieure peut se faire par optimisation, mais passer à une échelle 10 fois supérieure nécessite une réarchitecture
    • Ninja a été conçu autour du build de Chrome, qui comptait alors environ 30 000 étapes de build
    • Il est aujourd’hui aussi utilisé dans des environnements plus petits, mais dans ces cas l’avantage de vitesse peut ne pas être nécessaire
    • Dans des environnements plus grands comme le build d’Android, il atteint des limites de scalabilité, et une autre approche pourrait être nécessaire
    • Pour l’exécution parallèle, Ninja exige que l’utilisateur fournisse suffisamment d’informations, mais il n’impose pas de connaître entièrement tout le build
    • Il existe des compromis entre exactitude, confort d’utilisation et performance ; à l’échelle de l’écosystème, un outil qui sacrifie une part d’exactitude au profit de la commodité peut parfois produire de meilleurs résultats qu’un outil plus exact mais moins pratique

La séparation créée par la métaphore de « l’assembleur »

  • Les systèmes de build fournissent de nombreuses fonctionnalités de haut niveau, et la manière dont chaque outil se décrit est si vaste qu’il est difficile de les comparer entre eux
  • L’intuition clé de Ninja est que, quelles que soient les fonctionnalités de haut niveau fournies, un système de build doit finir par construire un graphe d’actions pour maintenir les fichiers à jour
  • Ninja n’exécute que ce graphe d’actions et laisse à l’utilisateur le choix du générateur à placer au-dessus
  • La séparation entre ces deux programmes avait été conçue à l’origine parce qu’elle convenait bien au projet Chrome, mais elle est ensuite devenue la contribution la plus importante de Ninja
  • Les avantages sont doubles
    • Ninja lui-même reste simple et rapide
    • Les opérations coûteuses, comme faire un glob sur *.c, sont repoussées du côté du générateur
  • Contrairement aux autres systèmes de build qui traitent tout en une seule fois, la structure de Ninja conduit à enregistrer sur disque un instantané du graphe d’actions calculé
  • En conséquence, le graphe d’actions est mis en cache entre deux builds
  • Le générateur peut être aussi haut niveau qu’on le souhaite
    • Par exemple, il est possible de faire un glob sur tout l’arbre des sources et de considérer comme tests les fichiers dont le nom contient test
  • Le développeur doit décider lui-même ce pour quoi il veut payer en coût d’exécution
    • Si le générateur fait un glob sur tout le disque, c’est possible, mais la raison pour laquelle le build est lent devient aussi plus évidente
  • La séparation entre générateur et graphe d’actions n’est pas simple en pratique, et Ninja contient aussi de nombreux détails sur la couche qui doit prendre en charge telle ou telle tâche
  • Les systèmes de build de Xcode ou Visual Studio pourraient théoriquement fonctionner de la même manière, en précalculant puis en prenant un instantané du résultat, mais la tentation de mélanger les couches empêche selon moi que cela fonctionne bien
  • Make a tenté d’inclure toutes les fonctionnalités destinées aux programmeurs, comme le globbing, l’expansion de variables, les substrings et les fonctions ; le résultat est devenu un langage trop faible pour exprimer toutes les fonctionnalités nécessaires, mais suffisamment puissant pour écrire des Makefile lents
  • Ninja a cherché à éviter cette direction

Les valeurs par défaut dominent la performance ressentie

  • Par défaut, Ninja exécute les commandes souhaitées en parallèle
  • Make permet aussi l’exécution parallèle avec le flag -j, mais son comportement par défaut est l’exécution séquentielle
  • Les Makefile sont facilement écrits dans un état où l’exécution parallèle n’est pas sûre, faute de dépendances suffisamment explicites
  • Ninja exécute toujours en parallèle, même sur les systèmes monocœur, ce qui fait apparaître ces erreurs plus tôt
  • Les programmes bien adaptés à Ninja deviennent généralement aussi sûrs pour les builds parallèles
  • Ninja ne dispose pas d’un système avancé pour détecter les dépendances incorrectes ; sa structure fait simplement apparaître les problèmes parce que les builds erronés surviennent plus souvent
  • Comme les utilisateurs oublient souvent le flag d’exécution parallèle de Make, ou ne le connaissent pas, une simple valeur par défaut peut suffire à faire paraître Ninja « plus de deux fois plus rapide » que Make en usage réel
  • Une optimisation que l’utilisateur ne ressent pas réellement a peu d’intérêt

Les métriques importantes pour la vitesse

  • La performance d’un système de build peut avoir plusieurs sens
    • Le temps nécessaire à un build complet depuis zéro
    • Le temps nécessaire à recompiler après avoir déjà buildé puis modifié un seul fichier
  • Ninja se concentre sur l’accélération de la boucle édition-compilation dans les builds incrémentaux de grandes bases de code
  • Au moment de créer Ninja, le souvenir de la grande rapidité de blaze, c’est-à-dire bazel, a servi de cible à atteindre
  • Avec le recul, blaze n’était pas particulièrement rapide sur la métrique qui importait, et comme c’était un programme Java, même l’affichage de l’aide était lent
  • L’obsession pour les builds incrémentaux venait de l’idée que le temps d’itération influence fortement la satisfaction des programmeurs
  • Ninja est utilisé dans la boucle édition-compilation, où la différence entre 1 seconde et 4 secondes compte
  • Le sens de « rapide » est difficile à transmettre aux utilisateurs, et le manuel de Ninja avertit que l’effet de vitesse peut être à peine perceptible sur les petits projets
  • Mais « rapide » est un argument qui se vend bien, si bien que des utilisateurs de petites applications, frustrés par le manque de fonctionnalités, veulent tout de même utiliser Ninja
  • Ninja s’est concentré sur les rebuilds incrémentaux, mais certains utilisateurs signalent aussi une amélioration des performances de build complet
    • Parce que Ninja ne fait presque rien, il utilise peu de CPU pendant le build et en soustrait moins aux véritables tâches de compilation
  • La sortie est aussi très concise
    • Un build réussi n’affiche généralement qu’une seule ligne
    • D’autres systèmes de build affichent beaucoup d’étapes et d’informations de timing, ce qui peut donner une impression de lourdeur
  • Sa tendance à peu parler rend Ninja plus « invisible »

L’intégration à CMake et la prise en charge de Windows ont amplifié l’adoption

  • CMake

    • Ninja a été créé à l’origine pour être utilisé avec le système de build spécifique de Chrome
    • Peter Collingbourne a réalisé le travail consistant à connecter Ninja au système de build CMake, bien plus largement utilisé
    • Cette intégration a commencé pour les besoins de LLVM, et il a fallu ajouter de nouvelles sémantiques non seulement à CMake, mais aussi à Ninja
    • Peter est cité comme la personne qui a le plus contribué au succès réel de Ninja
    • Les auteurs de CMake ont ensuite repris l’intégration, mais les demandes et préoccupations n’ont pas reçu de réponse suffisante
    • Le développeur n’a jusqu’à présent jamais utilisé directement CMake
  • Windows

    • Comme Chrome ciblait aussi Windows, Ninja a également été rendu compatible avec Windows
    • Une grande partie de la prise en charge de Windows a été écrite par un contributeur
    • Techniquement, la prise en charge de Windows représente une grande source de complications
    • L’exécution de processus et la capture de sortie font partie de ces différences qui obligent à apprendre de nouvelles API propres à chaque plateforme
    • La conception de Ninja repose sur la propriété selon laquelle on peut récupérer rapidement les dates de dernière modification des fichiers mises en cache par le noyau ; sous Windows, ce n’est pas vrai
    • Windows reste néanmoins une plateforme comptant un grand nombre de développeurs
    • Pour les bons outils Linux, l’envie de partager est forte, tandis que pour les outils Windows, l’envie de vendre est forte ; les outils proposés librement y sont donc relativement moins nombreux
    • Le fait que beaucoup des premiers utilisateurs de Ninja soient sous Windows était surprenant à l’époque, mais vu le grand nombre de développeurs Windows, même une petite fraction intéressée devient visible parmi les utilisateurs

Comprendre les travaux connexes

  • Ninja a commencé comme un hack de démonstration de week-end ; le regret est donc de ne pas avoir suffisamment étudié les travaux connexes avant de le créer
  • Lorsqu’on construit quelque chose, il est important de réellement comprendre l’espace de conception
  • Le terme « action graph » n’est pas celui qui était utilisé lors de la création de Ninja, mais vient du système de build de Google, blaze/bazel
  • bazel explique qu’un graphe de cibles de haut niveau comme « library » ou « binary » génère un action graph, un graphe de commandes
  • L’idée de considérer le texte de la ligne de commande comme une entrée semblable à un fichier est un cas de calcul incrémental
  • Le calcul incrémental concerne non seulement les systèmes de build, mais aussi l’incrémentalité des interfaces utilisateur
  • Le blog de Jane Street propose un article d’introduction à Incremental, qui fait aussi le lien avec des modes de construction d’interface comme React
  • "Build Systems à la Carte" est un article qui traite du calcul incrémental dans le contexte des systèmes de build ; c’est le genre de ressource qui aurait été utile avant d’écrire Ninja

La charge de la maintenance open source

  • L’expérience de mainteneur open source n’a pas été particulièrement agréable
  • Le ressenti vis-à-vis du projet mêle la fierté quand quelqu’un en dit du bien et une déception plus grande encore
  • Le fait de l’avoir publié gratuitement a amené parfois des personnes à formuler des demandes gentiment, mais plus souvent des utilisateurs en colère à exiger des choses, tandis que les remerciements étaient rares
  • Certaines personnes menaçaient régulièrement de forker le projet si leurs demandes n’étaient pas acceptées
  • Les contributions raisonnables mais en conflit avec les objectifs de conception représentaient une autre difficulté
    • Lorsqu’il fallait refuser la contribution d’une personne aimable et intelligente, l’envie de répondre par une explication suffisante était forte, et cette explication elle-même devenait épuisante
  • Le développeur est venu à la programmation grâce au logiciel libre et a écrit du code avec l’envie de rendre ce qu’il avait reçu, mais le logiciel libre actuel ressemble moins à un partage entre égaux qu’à une dynamique où les utilisateurs se considèrent comme des clients et traitent l’auteur comme quelqu’un dont on peut se plaindre au manager
  • La motivation actuelle tient moins au fait de connaître un succès massif qu’à l’envie d’impressionner un petit nombre de hackers respectés ou de répondre à leurs attentes
  • Le succès de Ninja a permis d’apprendre beaucoup, mais un succès plus modeste aurait sans doute permis d’apprendre des choses similaires

Mainteneurs et contributeurs

  • Nico Weber a été un collaborateur prudent et a maintenu Ninja pendant plusieurs années
  • Jan Niklas Hasse a repris le projet après Nico et semble bien s’en sortir
  • De nombreux autres contributeurs de Ninja ont également participé au projet

1 commentaires

 
GN⁺ 2024-11-30
Avis sur Hacker News
  • La formule selon laquelle « on parle de programmation comme d’écriture de code, mais en réalité l’architecture devient plus importante que le code, et les enjeux sociaux plus importants que l’architecture » exprime exactement une idée que j’avais en tête depuis longtemps

    • Tout à fait d’accord. Peopleware 1987 [1] dit la même chose
      Le chapitre 1 du livre affirme que « les principaux problèmes de notre travail sont de nature sociologique plutôt que technique », et traite de questions sociales et politiques comme l’alchimie et la cohésion d’équipe, le « temps de concentration », un environnement de travail calme et le coût du turnover
      [1] https://en.wikipedia.org/wiki/Peopleware:_Productive_Project...
    • La programmation et ses outils sont faits pour les humains, pas pour les ordinateurs, et sauf pour des tâches très triviales, créer quelque chose qui tourne sur un ordinateur exige plusieurs personnes
      C’est pourquoi je considère que le génie logiciel est bien plus proche des sciences sociales que beaucoup ne veulent l’admettre ou ne s’en sentent à l’aise. Les sciences sociales sont plus fluides et moins prévisibles que les sciences naturelles, d’où, semble-t-il, une tendance à éviter la dimension sociale ou à ne la traiter que de façon très rudimentaire. On s’obsède sur les détails atomiques de la technique, alors même que le produit construit par l’équipe finit trop souvent par être médiocre
    • https://en.wikipedia.org/wiki/Conway's_law
      « Les organisations qui conçoivent des systèmes sont contraintes de produire des conceptions qui reproduisent les structures de communication de ces organisations. » — Melvin E. Conway, How Do Committees Invent?
    • D’après mon expérience, environ 80 % des problèmes techniques viennent simplement du fait que deux personnes ou deux équipes rechignent à s’asseoir ensemble pour se parler
    • Ce n’est pas pour autant une dichotomie. Une bonne architecture satisfait ce que les gens attendent du système tout en le maintenant compréhensible par des humains
  • Cela avait déjà été discuté à l’époque :
    The Success and Failure of Ninja - https://news.ycombinator.com/item?id=23157783 - mai 2020, 38 commentaires
    Au bout d’environ un an, un repost est acceptable, et le lien vers l’ancien fil est là pour les lecteurs qui veulent en savoir plus

  • Le passage disant qu’« Android utilise Ninja pour un gros composant du système, mais je n’ai jamais vraiment compris lequel » est amusant
    Ninja occupe en réalité une place importante dans AOSP. Le système de build utilisait au départ des Makefile, mais il est vite devenu complexe à cause de soong, un système de build déclaratif maison, et d’une migration vers Bazel qui a échoué et été interrompue. Google a créé kati (https://github.com/google/kati), qui transforme les Makefile en fichiers de build Ninja, et le résultat est vraiment énorme :
    λ wc -l out/build-qssi.ninja
    3035442 out/build-qssi.ninja
    Le passage de Makefile/soong à Ninja est pénible et prend plusieurs minutes même sur des machines récentes, mais une fois que Ninja a pris le relais, ça tourne extrêmement vite

    • Je n’ai jamais utilisé Ninja, et je me demande quels avantages il a par rapport aux Makefile
      Je me demande aussi s’il vaut la peine d’introduire un autre outil pour traduire d’un outil à un autre. Surtout si les fichiers Ninja peuvent être aussi volumineux et difficiles à lire pour un humain
  • À propos du passage « je crois que les programmeurs ressentent la latence et qu’elle affecte leur humeur même s’ils ne s’en rendent pas compte. Google a récemment mené une étude dans ce domaine, qui a en partie confirmé ma conviction », je me demande si cette étude sur la latence a vraiment été publiée

  • Ninja est assez populaire chez les développeurs de jeux
    J’ai trouvé intéressante la phrase « Windows reste une énorme plateforme côté développeurs, et ces développeurs manquent cruellement d’outils ». En tant que personne qui développe principalement sous Windows, j’ai plutôt l’impression que ce sont les développeurs Linux, privés d’un bon débogueur comme Visual Studio ou d’un profiler comme Superluminal, qui manquent d’outils. Ces derniers temps, l’écart entre les deux plateformes semble se réduire, et j’aime aussi les utilitaires Rust qui fonctionnent globalement en multiplateforme

    • Chaque fois que quelqu’un met Visual Studio et « bon » dans la même phrase, j’ai cette étrange impression que l’univers est une simulation et que les autres viennent d’un autre univers
      Je n’arrive pas à penser à une interaction avec Visual Studio que je qualifierais de « bonne ». Quelques-unes pourraient être « à peine correctes », mais le débogage n’en fait pas partie. Cela dit, dans la version 2022, le débogueur n’est peut-être plus truffé de bugs, c’est peut-être de cela qu’il est question
    • Je ne sais pas dans quel monde le débogueur de Visual Studio est considéré comme bon. Peut-être s’ils l’ont enfin corrigé ; la dernière fois que je l’ai utilisé, il était insupportablement lent, au point que l’exécution pas à pas d’une ligne prenait plusieurs secondes
    • Il existe des communautés qui voient UNIX comme l’alpha et l’oméga des outils de développement, et elles semblent alors voir la forêt mais manquer les arbres
      Je connais bien UNIX, ayant utilisé diverses variantes depuis ma découverte de Xenix en 1993, mais je n’utilise presque jamais WSL, sauf pour faire tourner des conteneurs Linux Docker
  • Certains des éléments qui utilisaient encore Ninja ont été remplacés par samurai, avec des améliorations sur tous les aspects possibles
    Malgré tout, je pense que ce type de système de build fait fausse route. Ce que j’attends d’un système de build, c’est qu’il hache le contenu de toutes les entrées transitives, puis vérifie si le résultat existe dans un registre

    • Exact. Les systèmes de build qui prennent en charge un cache distribué utilisent en général des digests plutôt que des horodatages pour détecter les changements : Bazel, Pants, Buck, etc.
      Cela dit, ils sont tous extrêmement complexes. Pour les builds locaux, il me semble que SCons et Waf utilisent aussi des hachages pour détecter les changements
    • n2, créé par l’auteur de Ninja, pourrait t’intéresser
    • Je crois que c’était l’idée de NetKernel
      Dans le même esprit, j’ai créé une bibliothèque Deno, « TDAR »[1], et elle fonctionne bien, mais il faut un peu de travail pour envelopper des outils en ligne de commande — qui supposent fonctionner sur un système de fichiers mutable — comme si on les appelait en tant que fonctions pures
      [1] Elle n’a pas encore été extraite du projet parent[2], mais j’en ai parlé dans cette vidéo YouTube : https://youtu.be/sty29o8sUKI
      [2] Si ce genre de choses vous intéresse, vous pouvez me pousser à publier le code source. togos zero zero at gee mail dot comb
    • Je me demande en quoi Samurai est meilleur. Je pensais que c’était un sous-ensemble compatible de Ninja
      Et « ce n’est pas ce que je voulais » ne signifie pas forcément « c’est mauvais ». Il y a aussi des gens aux goûts différents dans le monde
    • Je ne sais pas si Samurai est encore vivant. J’ai envoyé une pull request améliorant la gestion des signaux, et elle est ignorée depuis plus de six mois
  • Comme il faut Ninja pour utiliser les modules C++20 avec CMake, Ninja va probablement rester longtemps

  • Le passage le plus intéressant était celui-ci : « il faut souvent faire des compromis entre exactitude et commodité ou performance, et il faut être conscient de l’endroit où l’on se place sur ce continuum »
    Certains programmeurs voient cette dynamique de façon trop rigide et pensent qu’une valeur doit évidemment primer sur l’autre. Mais, en pratique, l’interaction est assez subtile. Par exemple, un outil qui sacrifie un peu d’exactitude au profit de la commodité peut produire de meilleurs résultats pour l’ensemble de l’écosystème qu’une alternative plus exacte mais moins pratique. C’est le cas si les programmeurs finissent par éviter cette dernière

    • C’est vraiment une mine d’or comme intuition. C’est pour cela que Python, Go, TypeScript/JavaScript sont bien plus populaires que Haskell/OCaml
  • « Pardonnez le nom embarrassant » : au contraire, le nom est excellent
    P.-S. : implémenter ceci pourrait aussi le rendre plus rapide : https://github.com/ninja-build/ninja/issues/2157 ; cependant, comme l’explique l’article, cet outil est volontairement sans état, jusqu’à exclure même les plus petits indices issus de l’exécution précédente