Journal Utiliser Python comme interpréteur dans vos Makefile

Posté par (page perso) . Licence CC by-sa
16
12
jan.
2016

Le système de construction de logiciel utilisé par un projet est souvent celui proposé par le langage principal: setuptools pour Python, rebar pour Erlang, maven pour Java, etc. GNU make a l'avantage d'être vraiment indépendant de tous les langages mais tellement générique qu'il est rarement utilisé sans surcouche: scons, cmake, autotools, etc.

GNU make a un autre avantage qu'aucun autre système, à ma connaissance, n'a qui est son langage propre (DSL) qui permet de déclarer de manière claire les dépendances entre cibles. Il permet, entre autre, de paralléliser très facilement les constructions et d'étendre très facilement des Makefile existants.

Le gros problème de GNU make, pour moi, est de devoir écrire les règles en sh, ce qui oblige à:
* dépendre d'un tas d'outils externes et rien dans make ne permet de gérer ces dépendances facilement,
* jongler avec la syntaxe de sh, ses single-quote, double-quote et cie.

Suite à une discussion de pause de café, j'ai imaginé qu'on pourrait avoir le meilleur de GNU make et de vrais langages de programmation en remplaçant simplement le shell utilisé par make par un interpréteur.

Pour l'exemple, j'ai choisi Python et le résultat se trouve ici: python.mk.

  • # Re: Journal— Utiliser Python comme interpréteurdans vos Makefile

    Posté par (page perso) . Évalué à 4.

    as tu regardé invoke et fabric ?
    http://docs.pyinvoke.org/en/0.11.1/getting_started.html

  • # $(SHELL)

    Posté par . Évalué à 10. Dernière modification le 12/01/16 à 19:12.

    Bonsoir,

    Pourquoi réinventer la roue, alors que GNU make permet déjà de spécifier le shell à utiliser :

    SHELL = /usr/bin/env python
    all:
        @print "Hello World!"
    

    Ref. manuel de GNU make

    Hop,
    Moi.

    • [^] # Re: $(SHELL)

      Posté par . Évalué à 6.

      heu.. mais c'est ce qu'il fait hein ;-)
      Bonjour les perfs par-contre, vu qu'avec cette méthode, toutes les règles utilisent python (+ un shell pour lancer un "wrapper" dont je me demande s'il ne serait pas possible de se passer tout simplement). Pourquoi ne pas écrire une fonction ad-hoc pour GNUMake ? I.e. genre $(PYTHON "mon on-liner").
      Il faut bien se render compte aussi qu'il n'y a pas d'état persistent, donc on ne peut pas manipuler l'état de l'interpréteur dans une règle pour le réutiliser dans une autre. Ça pourrait être amusant d'embedder un deuxième language de script dans make, en Lua cela devrait-être assez facile… (par contre no-comment pour la "maintenabilité").

      • [^] # Re: $(SHELL)

        Posté par (page perso) . Évalué à 3.

        Oui, si tu regardes bien, le wrapper sert juste à gérer un pseudo PYTHONSTARTUP, car en ligne de commande, Python ne lit pas ce fichier PYTHONSTARTUP. Ceci permet d'éviter que toutes les lignes du make commencent par "import os; import …".

        En terme de perfs, je dirais que la perf n'est pas une contrainte énorme des système de build, en général. Si vraiment on a besoin de perf, GNU make permet de paralléliser les tâches très facilement. Est-ce le cas de setuptools ? maven ?

        De plus, je crains que tu oublies tous les forks qu'on ne voit pas ;)

        1. Quand on utilise sh avec make, il ne faut pas oublier qu'il y a, pour chaque ligne au moins un fork (sh), c'est toujours le cas ici.
        2. Avec un "one-liner", ça sera pareil, c'est juste la syntaxe qui change.
        3. Dès qu'on utilise un sed, awk, des pipes etc, ce sont autant de forks…

        À la fin, beaucoup de traitements de chaînes de caractères, pour ne prendre que ce cas, seront faits avec un seul process (python) quand tu en aurais utilisé 3,4 ou plus avec un sh.

        • [^] # Re: $(SHELL)

          Posté par . Évalué à 2.

          En terme de perfs, je dirais que la perf n'est pas une contrainte énorme des système de build, en général. Si vraiment on a besoin de perf, GNU make permet de paralléliser les tâches très facilement.

          C'est pourtant ce qui a motivé l'écriture de Ninja. Même avec un build bien parallélisé, un système de build performant peut aider, en particulier dans un processus d'intégration continue.

        • [^] # Re: $(SHELL)

          Posté par . Évalué à 2.

          Est-ce le cas de setuptools ? maven ?

          Je ne connais pas setuptool, mais pour maven ça s'explique très bien. Ce n'est pas la même granularité. Là où en C/C++/… tu va lancer un processus de ton compilateur par fichier que tu souhaite produire en java tu va lancer une fois le compilateur pour tous tes fichiers sources. Si on ajoute à ça la compilation de Java bien plus rapide que celle d'un fichier en C et pire encore en C++, tu verra que tu n'a que peu d'intérêt à paralléliser dans ton système de build la compilation. Pour les autres tâches par contre ça peut avoir un intérêt.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: $(SHELL)

            Posté par (page perso) . Évalué à 3.

            Est-ce le cas de setuptools ? maven ?

            Je ne connais pas setuptool, mais pour maven ça s'explique très bien. Ce n'est pas la même granularité. Là où
            en C/C++/… tu va lancer un processus de ton compilateur par fichier que tu souhaite produire en java tu va
            lancer une fois le compilateur pour tous tes fichiers sources. Si on ajoute à ça la compilation de Java bien
            plus rapide que celle d'un fichier en C et pire encore en C++,

            Tu as des chiffres ? Tu parles de gcc ? De clang ?

            tu verra que tu n'a que peu d'intérêt à
            paralléliser dans ton système de build la compilation.

            (attention, troll inside)

            C'est juste une impression personnelle ou les développeurs Java ont une forte tendance à dire que ce que Java ne fait pas n'a pas d'intérêt ?

            Les rares fois où j'ai été obligé d'utiliser Maven, c'est justement là où je me suis dit que la perf peut vraiment avoir une importance dans un système de build. C'est abominablement lent…

            Pour les autres tâches par contre ça peut avoir un intérêt.

            • [^] # Re: $(SHELL)

              Posté par . Évalué à 6.

              Tu as des chiffres ? Tu parles de gcc ? De clang ?

              Tu build ton projet avec make avec des règles du genre :

              fic.o: fic.h fic.c
                  cc...

              Pour chaque fichier que tu génère tu lance ton compilateur. Ce n'est pas péremptoire, mais un fait.

              C'est juste une impression personnelle ou les développeurs Java ont une forte tendance à dire que ce que Java ne fait pas n'a pas d'intérêt ?

              Pas du tout, Java a pleins de problèmes. Je peux te parler du go si tu préfère. En go tu n'a autant besoin de faire de la parallélisation parce que tu lance une fois ton compilateur pour l'ensemble de tes sources. javac est très rapide (et vu le peu de chose qu'on lui demande de faire c'est encore heureux) là où gcc est très lent comparativement parce qu'il ne traite q'une seule unité de compilation par lancement, parce que la manière de gérer les entête par inclusion est très lente. En c++ c'est encore pire si tu utilise les templates de manière un peu poussée.

              Mais je suis d'avis qu'il vaut mieux une compilation un peu plus lente et une vitesse d'exécution d'enfer que l'inverse.

              Les rares fois où j'ai été obligé d'utiliser Maven, c'est justement là où je me suis dit que la perf peut vraiment avoir une importance dans un système de build. C'est abominablement lent…

              La parallélisation ne changera rien. Maven n'est pas très rapide parce qu'il est con, parce qu'il n'a pas de cache entre chaque lancement, parce que les dev s'en foutent,… Ce n'est pas un problème de parallélisme. Intéresse toi à la question, tu verra de toi même ce n'est pas la parallélisation qui le ralenti.

              Faut pas se focaliser, la parallélisation n'est pas la seule réponse à la lenteur.

              Tiens au fait pour ton troll. Il y a pas mal de projet C++ qui prennent en compte la performance de leur build lors du développement (en limitant le nombre de fichiers, en effectuant plus de choses directement dans les headers, en modifiant leur build pour utiliser des entêtes précompilées,…), je n'ai jamais vu un projet en java avoir besoin de ce genre de chose (mais remplace java par go dans tout ce que je dis ça restera vrai).

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: $(SHELL)

            Posté par . Évalué à 1.

            tu verra que tu n'a que peu d'intérêt à paralléliser dans ton système de build la compilation.

            Ca dépends, sur le projet actuel et avec nos makefiles actuels on en a pour 20 minutes de compilation avec du distcc

            A coup de cmake fait à l'arrache on aboutit à une compile de 10 minutes.

            Il ne faut pas décorner les boeufs avant d'avoir semé le vent

            • [^] # Re: $(SHELL)

              Posté par (page perso) . Évalué à 4.

              Dans la mesure où barmic parle du build d'un projet de l’écosystème Java qui a peu d'intérêt à paralléliser par rapport à un projet C/C++, et dans la mesure où tu réponds que ton projet C/C++ (je me trompe peut-être mais makefile + distcc + cmake… ça sent pas trop le Java) tire partie de parallélisation, je pense que tu es hors-sujet.

        • [^] # Re: $(SHELL)

          Posté par (page perso) . Évalué à 6. Dernière modification le 13/01/16 à 17:20.

          À la fin, beaucoup de traitements de chaînes de caractères, pour ne prendre que ce cas, seront faits avec un seul process (python) quand tu en aurais utilisé 3,4 ou plus avec un sh.

          C'est le signe que tu te trompes d'utilisation de make. Le programme make sert à décrire des tâches dont l'état d'avancement est entièrement décrit par l'état du système de fichiers. Si ce n'est pas le cas, on perd les bénéfices de make qui sont la facilité d'interrompre une tâche ou le parallélisme.

          Le traitement que tu accomplis à grand coup de awk sed etc. doit donc probablement être remplacé par un programme qui écrit son résultat dans un fichier. Le cas échéant ce fichier peut être un fragment de Makefile valide que tu peux inclure avec la directive include.

          De mémoire, GNU Make traite spécialement la cible .depends pour gérer ce cas de figure – mais je peux me tromper car je suis utilisateur de BSD Make. :)

        • [^] # Re: $(SHELL)

          Posté par . Évalué à 0.

          Quand on utilise sh avec make, il ne faut pas oublier qu'il y a, pour chaque ligne au moins un fork (sh), c'est toujours le cas ici.

          Non justement, GNUMake exécute SHELL…

          Dès qu'on utilise un sed, awk, des pipes etc, ce sont autant de forks…

          L'idée c'est d'utiliser les fonctions de make pour manipuler le one-liner avec les $(subst …) et autres. https://www.gnu.org/software/make/manual/html_node/Text-Functions.html#Text-Functions

          Sorry de n'avoir pas été super explicite sur ce coup, ce que je voulais dire c'est:
          1) Pourquoi overrider SHELL pour au final lancer un sh pour faire des manipulation de chaînes qui pourraient tout a fait être faites par Make et ce faisant économiser l'exécution superflue d'un sh. Et le corrolaire 2): tant qu'à quand même utiliser sh, pourquoi pas se garder d'overrider SHELL mais utiliser à la place une fonction make $(PYTHON …) afin de pouvoir garder le meilleurs des deux mondes ?

          • [^] # Re: $(SHELL)

            Posté par (page perso) . Évalué à 2.

            1) Pourquoi overrider SHELL pour au final lancer un sh pour faire des manipulation de chaînes qui pourraient tout a fait être faites par Make et ce faisant économiser l'exécution superflue d'un sh. Et le corrolaire 2): tant qu'à quand même utiliser sh, pourquoi pas se garder d'overrider SHELL mais utiliser à la place une fonction make $(PYTHON …) afin de pouvoir garder le meilleurs des deux mondes ?

            Exactement, pour générer des données textuelles nécessitant un traitement un tant soit peu compliqué, on peut très bien écrire une petit script, qu'on va soit placer dans un dossier du projet, soit dans une variable (pour les scripts de une et quelques lignes).

            Exemple.

  • # Python3

    Posté par (page perso) . Évalué à 8.

    all:
        print "$(DIR)"

    Du coup, c'est du python2, ça marche avec python3 ?

    « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

    • [^] # Re: Python3

      Posté par (page perso) . Évalué à 1.

      Je ne connais pas Python3. Le wrapper utiliser 'python', donc la version par défaut sur ton système. La syntaxe $(DIR) sera interprétée par make avant d'être passée à Python.

      • [^] # Re: Python3

        Posté par . Évalué à 2.

        Je pense qu'il fait référence au fait qu'en Python 3, print n'est plus un mot clé du langage mais une fonction :

        # Python 2
        print "hello, world"
        
        # Python 3
        print("hello, world")

        Xavier Claude<, j'ai fait le test suivant, ça répond à ta question ?

        [killou@fantasmic tmp]$ cat Makefile
        SHELL = /usr/bin/env python3
        
        name=world
        
        all:
                @print("hello, $(name)")
        
        [killou@fantasmic tmp]$ make
        hello, world
        
  • # scons

    Posté par (page perso) . Évalué à 7.

    il est rarement utilisé sans surcouche: scons, cmake, autotools, etc.

    scons n'est pas une surcouche à make, c'est du 100% python

  • # Gradle

    Posté par . Évalué à 3.

    GNU make a un autre avantage qu'aucun autre système, à ma connaissance, n'a qui est son langage propre (DSL) qui permet de déclarer de manière claire les dépendances entre cibles.

    gradle le fais encore mieux je trouve, il sépare la définition des tâches de leurs dépendances entre elles. Dans cet exemple je crée une tâche stage et je la fais dépendre de installDir

    https://github.com/barmic/cotize/blob/master/build.gradle

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Jam

      Posté par (page perso) . Évalué à 3.

      Il y a aussi l'outil Jam de Perforce (utilisé par Boost et Haiku, entre autres), qui permet de faire tout ce que fait Make, sans utiliser sh, et avec son propre langage (ce qui n'est pas forcément mieux).

      • [^] # Re: Jam

        Posté par . Évalué à -5.

        Euh … :
        https://duckduckgo.com/?q=gradle&t=ffsb&ia=about

        Gradle est un moteur de production fonctionnant sur la plateforme Java.

        Beurk, poubelle.

        • [^] # Re: Jam

          Posté par (page perso) . Évalué à 2.

          Tu sais pas que c'est mal d'être raciste?

          http://devnewton.bci.im

        • [^] # Re: Jam

          Posté par . Évalué à 3.

          Ça t'interdit de regarder sa syntaxe ?

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Jam

            Posté par . Évalué à -4. Dernière modification le 13/01/16 à 19:06.

            Ben les outils Java de ce genre ont en général une syntaxe à vomir, et je ne tiens pas à salir mon clavier.

            Je précise que je parle de gradle, pas de jam.

            • [^] # Re: Jam

              Posté par (page perso) . Évalué à 3.

              Cet outil qui utilise un langage bien plus concis, expressif et efficace que ceux des autres outils de build (oui parce que Gradle est en Groovy, pas en Java) résout un paquet de problèmes, pour certains vieux de plusieurs décennies (apparemment ici on s’accommode de l'écriture de règles en make, mais je préfèrerais me bouffer les burnes en salade que d'écrire une ligne de make), mais comme quelque part dans la chaîne il y a un élément (ie. JVM) dont on m'a dit du mal sur l'incubateur d'innovations de référence (ie. DLFP) je le rejette

              Signé les dinosaures domiciliés à Chicxulub

        • [^] # Re: Jam

          Posté par . Évalué à -2.

          Je viens de me rendre compte que j'avais répondu au mauvais message (1 cran trop bas dans la hierarchie) : je parle de gradle, pour ceux qui n'auraient pas compris.

  • # dépendances entre cibles

    Posté par (page perso) . Évalué à 3.

    GNU make a un autre avantage qu'aucun autre système, à ma connaissance, n'a qui est son langage propre (DSL) qui permet de déclarer de manière claire les dépendances entre cibles. Il permet, entre autre, de paralléliser très facilement les constructions et d'étendre très facilement des Makefile existants.

    Je trouve GNU make au contraire assez limité de ce côté là, il manque par exemple un prédicat pour tester si une cible est déjà définie ou pas, ainsi on ne peut que difficilement définir des recettes générales en laissant l'utilisateur fournir sa propre définition s'il le souhaite.

    Le programme BSD Make est plus facile à programmer, je trouve. J'écris BSD Owl avec.

    Si tu n'a pas peur des programmes dont la communauté d'utilisateurs est très restreinte, je te conseille d'utiliser omake une variante de make écrite en OCaml, qui pour le coup a un DSL qui est vraiment flexible.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.