63 votes

Quelle est votre expérience de la fabrication non récursive ?

Il y a quelques années, j'ai lu le La fabrication récursive est considérée comme nuisible et j'ai mis en œuvre cette idée dans mon propre processus de construction. Récemment, j'ai lu un autre article avec des idées sur la façon de mettre en œuvre un système non récursif make . Donc, j'ai quelques points de données que les non-récursifs make fonctionne pour au moins quelques projets.

Mais je suis curieux de connaître les expériences des autres. Avez-vous essayé la méthode non-récurrente make ? Cela a-t-il amélioré ou aggravé les choses ? Cela valait-il la peine d'y consacrer du temps ?

1 votes

Au cas où cela serait utile à quelqu'un : Le lien vers l'article "Implementing non-recursive make" dans la question est maintenant 404s, mais l'article peut être trouvé à ce que je pense être le nouveau site web de l'auteur .

0 votes

Oh, et il semble este est peut-être un meilleur endroit pour trouver "Recursive Make Considered Harmful".

0 votes

@GarethMcCaughan : Merci d'avoir trouvé ces liens ! Je les ai édités dans le message. (Par curiosité, cela aurait-il aidé si la fonction d'édition était plus visible ou êtes-vous réticent à modifier les messages des autres ? J'essaie de me faire une idée de la façon dont nous pourrions améliorer le site. Danger de le travail ;-)

56voto

Ville Laurikari Points 10484

Nous utilisons un système GNU Make non récursif dans l'entreprise pour laquelle je travaille. Il est basé sur l'article de Miller et surtout sur le lien "Implementing non-recursive make" que vous avez donné. Nous avons réussi à affiner le code de Bergen pour en faire un système où il n'y a pas du tout de "boiler plate" dans les makefiles des sous-répertoires. Dans l'ensemble, cela fonctionne bien, et c'est bien mieux que notre système précédent (une chose récursive faite avec GNU Automake).

Nous prenons en charge tous les "principaux" systèmes d'exploitation existants (sur le plan commercial) : AIX, HP-UX, Linux, OS X, Solaris, Windows, et même le mainframe AS/400. Nous compilons le même code pour tous ces systèmes, les parties dépendantes de la plate-forme étant isolées dans des bibliothèques.

Il y a plus de deux millions de lignes de code C dans notre arbre, dans environ 2000 sous-répertoires et 20000 fichiers. Nous avons sérieusement envisagé d'utiliser SCons, mais nous n'avons pas réussi à le faire fonctionner assez rapidement. Sur les systèmes les plus lents, Python utilisait quelques dizaines de secondes pour analyser les fichiers SCons, alors que GNU Make faisait la même chose en une seconde environ. C'était il y a environ trois ans, donc les choses peuvent avoir changé depuis. Notez que nous conservons généralement le code source sur un partage NFS/CIFS et que nous construisons le code source de l'application même sur de multiples plateformes. Cela signifie qu'il est encore plus lent pour l'outil de construction d'analyser l'arbre source pour les changements.

Notre système non récursif GNU Make n'est pas sans problèmes. Voici quelques-uns des plus gros obstacles que vous pouvez vous attendre à rencontrer :

  • Le rendre portable, notamment sous Windows, représente un travail considérable.
  • Bien que GNU Make soit un langage de programmation fonctionnelle presque utilisable, il n'est pas adapté à la programmation en grand. En particulier, il n'y a pas d'espaces de noms, de modules, ou quoi que ce soit d'autre pour vous aider à isoler les éléments les uns des autres. Cela peut poser des problèmes, mais pas autant qu'on pourrait le penser.

Les principaux avantages par rapport à notre ancien système de makefile récursif sont les suivants :

  • C'est rapide . Il faut environ deux secondes pour vérifier toute l'arborescence (2 000 répertoires, 20 000 fichiers) et soit décider qu'elle est à jour, soit lancer la compilation. L'ancien système récursif prendrait plus d'une minute pour ne rien faire.
  • Il gère correctement les dépendances. Notre ancien système se basait sur l'ordre de construction des sous-répertoires, etc. Comme on pouvait s'y attendre à la lecture de l'article de Miller, traiter l'ensemble de l'arbre comme une entité unique est vraiment la bonne façon d'aborder ce problème.
  • Il est portable sur tous les systèmes que nous prenons en charge, après tout le travail que nous y avons consacré. C'est plutôt cool.
  • Le système d'abstraction nous permet d'écrire des makefiles très concis. Un sous-répertoire typique qui définit juste une bibliothèque ne comporte que deux lignes. Une ligne donne le nom de la bibliothèque et l'autre liste les bibliothèques dont celle-ci dépend.

En ce qui concerne le dernier point de la liste ci-dessus. Nous avons fini par implémenter une sorte d'extension de macro dans le système de construction. Les makefiles des sous-répertoires listent les programmes, les sous-répertoires, les bibliothèques et autres choses courantes dans des variables comme PROGRAMS, SUBDIRS, LIBS. Ensuite, chacune de ces variables est développée en "vraies" règles GNU Make. Cela nous permet d'éviter la plupart des problèmes liés aux espaces de noms. Par exemple, dans notre système, il est possible d'avoir plusieurs fichiers sources avec le même nom, il n'y a aucun problème.

En tout cas, cela a fini par représenter beaucoup de travail. Si vous pouvez faire fonctionner SCons ou un système similaire pour votre code, je vous conseille de le faire en premier.

13 votes

Je ne pense pas qu'un système de construction puisse vraiment égaler un système GNU make non récursif correctement écrit.

4 votes

@JesperE, essayez tup, et soyez surpris gittup.org/tup/make_vs_tup.html et bien sûr, Premake et cmake génèrent des makefiles automatiquement, donc je suppose qu'ils peuvent correspondre par définition.

0 votes

@Ville, je suis convaincu que faire du make non-récursif est possible. Mais je ne suis pas convaincu que c'est maintenable. Avez-vous dû utiliser sed pour les makefiles ? Combien de boilerplate avez-vous dû utiliser pour "inclure" un certain sous-projet ? Quelle est la facilité de compiler un seul sous-projet seul ? Avez-vous placé les fichiers de sortie (.c, .d) dans un répertoire distinct de celui des fichiers sources ?

30voto

Dan Moulding Points 46866

Après avoir lu l'article de RMCH, je me suis fixé pour objectif d'écrire un Makefile non récursif correct pour un petit projet sur lequel je travaillais à l'époque. Après avoir terminé, j'ai réalisé qu'il devrait être possible de créer un "framework" générique de Makefile qui peut être utilisé pour indiquer à Makefile de manière très simple et concise quelles cibles finales vous souhaitez construire, quel type de cibles elles sont (par exemple, des bibliothèques ou des exécutables) et quels fichiers sources doivent être compilés pour les réaliser.

Après quelques itérations, c'est ce que j'ai fini par créer : un Makefile unique d'environ 150 lignes de syntaxe GNU Make qui n'a jamais besoin d'être modifié -- il fonctionne pour tous les types de projets sur lesquels je veux l'utiliser, et il est suffisamment flexible pour construire plusieurs cibles de différents types avec suffisamment de granularité pour spécifier des drapeaux de compilation exacts pour chaque fichier source (si je le veux) et des drapeaux de liaison précis pour chaque exécutable. Pour chaque projet, tout ce que j'ai à faire est de lui fournir de petits Makefiles séparés qui contiennent des éléments similaires à ceux-ci :

TARGET := foo

TGT_LDLIBS := -lbar

SOURCES := foo.c baz.cpp

SRC_CFLAGS   := -std=c99
SRC_CXXFLAGS := -fstrict-aliasing
SRC_INCDIRS  := inc /usr/local/include/bar

Un Makefile de projet tel que celui ci-dessus ferait exactement ce que vous attendez : construire un exécutable nommé "foo", compiler foo.c (avec CFLAGS=-std=c99) et baz.cpp (avec CXXFLAGS=-fstrict-aliasing) et ajouter "./inc" et "/usr/local/include/bar" au fichier #include avec une liaison finale incluant la bibliothèque "libbar". Il remarquerait également la présence d'un fichier source C++ et saurait qu'il faut utiliser l'éditeur de liens C++ au lieu de l'éditeur de liens C. Le framework me permet de spécifier beaucoup plus que ce qui est montré ici dans cet exemple simple.

Le Makefile passe-partout fait tout le travail de construction des règles et de génération automatique des dépendances nécessaires pour construire les cibles spécifiées. Tous les fichiers générés par la compilation sont placés dans une hiérarchie de répertoires de sortie séparée, afin qu'ils ne soient pas mélangés avec les fichiers sources (et ceci est fait sans utiliser VPATH, il n'y a donc pas de problème à avoir plusieurs fichiers sources ayant le même nom).

J'ai maintenant (ré)utilisé ce même Makefile sur au moins deux douzaines de projets différents sur lesquels j'ai travaillé. Certaines des choses que j'aime le plus à propos de ce système (en dehors de la facilité avec laquelle il est possible de créer un fichier de type approprié Makefile pour tout nouveau projet) sont :

  • C'est rapide . Il peut dire presque instantanément si quelque chose est périmé.
  • Dépendances 100% fiables. Il n'y a aucune chance que les constructions parallèles soient mystérieusement interrompues, et les constructions sont toujours effectuées. exactement le minimum requis pour remettre tout à jour.
  • Je vais jamais J'ai besoin de réécrire un makefile complet à nouveau :D

Pour finir, je voudrais juste mentionner qu'avec les problèmes inhérents à la création récursive, je ne pense pas qu'il m'aurait été possible d'y arriver. J'aurais probablement été condamné à réécrire des makefiles défectueux encore et encore, en essayant en vain d'en créer un qui fonctionne correctement.

4 votes

Et pouvez-vous nous montrer votre makefile de base de 150 lignes ?

19 votes

@zxcat : Voir github.com/dmoulding/boilermake . Pour l'utiliser, vous devez fournir main.mk (et tout autre submakefile que vous souhaitez fournir). Consultez les fichiers README et MANUAL pour plus d'informations. Le répertoire test-app contient un exemple de main.mk et d'autres submakefiles qui construisent un petit programme de test simple et stupide. Amusez-vous bien.

0 votes

Votre makefile est impressionnant ! Corrigez-moi si je me trompe, mais il ne fait pas d'objets partagés, n'est-ce pas ? Ou plutôt, les objets partagés et la bibliothèque statique en même temps.

19voto

Pankrat Points 2145

Permettez-moi de souligner un argument de l'article de Miller : Lorsque vous commencez à résoudre manuellement les relations de dépendance entre les différents modules et que vous avez du mal à assurer l'ordre de construction, vous réimplémentez effectivement la logique que le système de construction était censé résoudre au départ. Construire des systèmes make build récursifs fiables est très difficile. Les projets de la vie réelle comportent de nombreuses parties interdépendantes dont l'ordre de construction n'est pas trivial à déterminer et donc, cette tâche devrait être laissée au système de construction. Cependant, il ne peut résoudre ce problème que s'il a une connaissance globale du système.

De plus, les systèmes de construction récursifs (make) ont tendance à s'effondrer lors de la construction simultanée sur plusieurs processeurs/cores. Bien que ces systèmes de construction puissent sembler fonctionner de manière fiable sur un seul processeur, de nombreuses dépendances manquantes ne sont pas détectées jusqu'à ce que vous commenciez à construire votre projet en parallèle. J'ai travaillé avec un système de construction récursif make qui fonctionnait jusqu'à quatre processeurs, mais qui s'est soudainement planté sur une machine avec deux quad-cores. Ensuite, j'ai été confronté à un autre problème : ces questions de concurrence sont pratiquement impossibles à déboguer et j'ai fini par dessiner un organigramme de l'ensemble du système pour comprendre ce qui n'allait pas.

Pour revenir à votre question, j'ai du mal à trouver de bonnes raisons pour lesquelles on veut utiliser le make récursif. Les performances d'exécution des systèmes de construction GNU Make non récursifs sont difficiles à battre et, bien au contraire, de nombreux systèmes make récursifs ont de sérieux problèmes de performance (le faible support de la construction parallèle est encore une partie du problème). Il existe un papier dans lequel j'ai évalué un système de construction Make spécifique (récursif) et l'ai comparé à un portage SCons. Les résultats en termes de performances ne sont pas représentatifs car le système de compilation n'était pas standard, mais dans ce cas particulier, le portage SCons était plus rapide.

En résumé : Si vous voulez vraiment utiliser Make pour contrôler la construction de vos logiciels, optez pour Make non-récursif car cela vous facilitera la vie à long terme. Personnellement, je préfèrerais utiliser SCons pour des raisons de facilité d'utilisation (ou Rake - en fait n'importe quel système de construction utilisant un langage de script moderne et ayant un support implicite des dépendances).

0 votes

Le problème de la concurrence m'affecte actuellement dans le cadre d'un projet sur lequel je travaille. J'ai pu activer la simultanéité de GNU make. à l'intérieur de mais je devrais gérer manuellement les relations inter-modules si je voulais la concurrence entre les modules. Le résultat est que je regarde une machine à 16 cœurs en moyenne moins de deux cœurs actifs pendant que je construis cette bête pendant plus de quarante minutes. Gah !

10voto

JesperE Points 34356

J'ai fait une tentative timide dans mon précédent emploi pour rendre le système de construction (basé sur GNU make) complètement non récursif, mais j'ai rencontré un certain nombre de problèmes :

  • Les artefacts (c'est-à-dire les bibliothèques et les exécutables construits) avaient leurs sources réparties dans un certain nombre de répertoires, s'appuyant sur vpath pour les trouver.
  • Plusieurs fichiers sources avec le même nom existaient dans différents répertoires
  • Plusieurs fichiers sources étaient partagés entre les artefacts, souvent compilés avec des drapeaux de compilation différents.
  • Des artefacts différents avaient souvent des drapeaux de compilateur différents, des paramètres d'optimisation différents, etc.

Une fonctionnalité de GNU make qui simplifie l'utilisation non-récursive est les valeurs des variables spécifiques à la cible :

foo: FOO=banana
bar: FOO=orange

Cela signifie que lors de la construction de la cible "foo", $(FOO) sera étendu à "banane", mais lors de la construction de la cible "bar", $(FOO) sera étendu à "orange".

L'une des limitations est qu'il n'est pas possible d'avoir des définitions de VPATH spécifiques à une cible, c'est-à-dire qu'il n'y a aucun moyen de définir le VPATH de manière unique pour chaque cible. Cela était nécessaire dans notre cas pour trouver les bons fichiers sources.

La principale caractéristique manquante de GNU make, nécessaire pour supporter la non-récursivité, est qu'il ne dispose pas de espaces de noms . Les variables spécifiques à la cible peuvent, de manière limitée, être utilisées pour "simuler" les espaces de noms, mais ce dont vous avez vraiment besoin est de pouvoir inclure un Makefile dans un sous-répertoire en utilisant une portée locale.

EDIT : Une autre fonctionnalité très utile (et souvent sous-utilisée) de GNU make dans ce contexte est la possibilité d'expansion de macros (voir la section eval par exemple). Ceci est très utile lorsque vous avez plusieurs cibles qui ont des règles/buts similaires, mais qui diffèrent d'une manière qui ne peut être exprimée en utilisant la syntaxe normale de GNU make.

3 votes

Utile : oui. Maintenable : non.

1 votes

C'est vieux de plusieurs années, mais vous mentionnez des variables spécifiques à la cible... danger, Will Robinson ! Ils ne font pas ce que vous attendez ! Lorsque la construction foo , FOO sera définie pour les deux foo et tous les prérequis construits par foo . Donc si foo y bar prendre la peine de dépendre baz si baz est construit avec FOO=orange o FOO=banana dépend de si make voit foo o bar premier. Cela finit par casser subtilement les constructions parallèles, qui ont tendance à être non déterministes.

0 votes

Aujourd'hui, je ne recommanderais en aucun cas GNU make comme outil de construction.

9voto

Adriaan Points 2481

Je suis d'accord avec les déclarations de l'article cité, mais il m'a fallu beaucoup de temps pour trouver un bon modèle qui fasse tout cela tout en restant facile à utiliser.

Actuellement, je travaille sur un petit projet de recherche, où j'expérimente l'intégration continue ; le test unitaire automatique sur PC, puis l'exécution d'un test système sur une cible (embarquée). Ceci n'est pas trivial dans make, et j'ai cherché une bonne solution. Constatant que make est toujours un bon choix pour les constructions multiplateformes portables, j'ai finalement trouvé un bon point de départ dans http://code.google.com/p/nonrec-make

Ce fut un véritable soulagement. Maintenant mes makefiles sont

  • très simple à modifier (même avec des connaissances limitées en matière de fabrication)
  • rapide à compiler
  • vérification complète des dépendances (.h) sans aucun effort

Je l'utiliserai certainement aussi pour le prochain (gros) projet (en supposant qu'il s'agisse de C/C++).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X