__ ______ __ __ __ _ ___ __ _ _ _ _\_ _ _ \ / \ / \ \ \ \ _ \ \ .\ | |/ / / / / _ / /.\ \ .- - / /\ \/ /\ \-\_\ \_\\_\-\_/ ----| < /_/-/_//_/\/__/ \- ------. : \ \ \__/ / / |_|\_\ \ \ : , \/ \/ \ \ | : \ \ . . 11.02 Présentation des makefiles \ \ ! . aka \ \ . ; You Make me File like an ASCII queen \ \ : : \ \ : '-- ----- - -------- --- --------- - -- ------------- --- -\ \-' \____ _ _\ ^^^^^^^ Ò] [] ~~~~ ____ | | _____ ______ || | | || || | A | || J`aimerais remercier tout spécialement =====| Z |===== Micheline SansZavnir qui m'a enduré | T | pendant mes longs cours de ... | | E | | ascii par correspondance. | | K | | |_ _| / ( || ) \ / /( ||) \ \ / (|| ) \ \ / / || \ / \ \ / / || ) \ \ / / \ / [01010101010] \ / / En passant pour ceux qui ne savent pas encore qu'est-ce que j'ai voulu dessiner c'est sans importance... dites vous juste que je suis 31337 ;P Présentation des makefiles. Vous aurez sûrement remarqué le makefile livré avec un programme lorsque vous l'avez compilé. Étant donné le niveau de complexité pouvant être atteint par un fichier make, vous pouvez à juste raison vous demander : "pourquoi utiliser make?" Si vous imaginez un instant un projet composé de dizaine, voir de centaines de modules, vous n'êtes sans doute pas très impatient de recompiler tous ces modules chaque fois que vous modifiez une ligne de code. Imaginez que vous devriez recompiler entièrement votre noyau à la main après une petite modification du code source. Il serait extrêmement difficile de déterminer les compilations à omettre, et l'omission de la compilation indispensable entraînerait des problèmes. C'est pourquoi la commande make est précisément destinée à ce genre de travail. La commande make est capable de tirer des conclusions sur les dépendances existantes entre divers fichiers. Elle possède d'autres fonctionnalités qui la rendent très puissante: elle sait définir des macros et des cibles supplémentaires qui vous permettront d'être plus efficace. Autre avantage des processus make : les instructions de compilation sont enregistrées dans le fichier make. Par exemple, il n'est pas nécessaire de mémoriser tous les paramètres de ligne de commande utilisés pour un projet. Mieux encore, il n'est pas nécessaire de documenter à l'intention d'autrui ce qui vous a servi à construire ce projet. >> Le Makefile Lorsque vous lancez la commande make sans désigner explicitement un fichier make sur la ligne de commande ( avec le paramètre -f), l'utilitaire GNU make recherche trois noms de fichiers par défaut dans le répertoire en cours, dans l'ordre suivant : 1. GNUmakefile 2. makefile 3. Makefile >> Nommer le Makefile Makefile Le nom Makefile avec la première lettre en majuscule convient le mieux, car lorsque vous listez vos fichiers, il figure en début de la liste. GNUmakefile La documentation de GNU make conseille d'éviter d'employer le nom de fichier GNUmakefile. Vous ne devez l'utiliser que si vous vous servez des foncionnalités gérées par la commande GNU make. Si le groupe de fonctionnalités employées est celui make UNIX standard, préférez Makefile ou makefile. makefile Lorsque le nom Makefile normal( avec un "M" majuscule) est utilisé, il vous reste une possibilité préalable. Si vous téléchargez un paquetage de sources de l'Internet, mais découvrez que vous avez besoin de modifier le Makefile fourni, vous pouvez tester vos modifications sans toucher à ce Makefile: 1. Recopiez Makefile en makefile. 2. Editez makefile selon vos besoins. 3. Lancez la commande make. C'est makefile qui est utilisé à la place de Makefile, car il est choisi en premier lorsqu'il existe. >> Les cibles des Makefile La commande make commence par la notion de cible. Lorsqu'elle possède un fichier make, la commande make doit traiter au moins une cible de ce fichier, sinon rien ne se passe ; et si le fichier ne contient aucune cible, la commande make se plaint de la situation : |#| bash$ make |#| make: *** No targets. Stop. |#| bash$ >> Définir les cibles Le listing 1.0 suivant présente un Makefile très simple pourvu de trois cibles, one, two, three, aux ligne 4, 7 et 10. Un nom de cible commence toujours au début d'une ligne et est suivi du caractère deux-points. _= Listing (1.0) Makefile simple comportant trois cibles =_ |#| 1: # Source: /home/acidmen/make/RCS/Makefile,v $ |#| 2: |#| 3: |#| 4: one: |#| 5: @echo One |#| 6: |#| 7: two: |#| 8: @echo two |#| 9: |#| 10: three: |#| 11: @echo three |#| 12: |#| 13: # End Makefile >> Invoquer les cibles de Makefile Pour invoquer une cible, il suffit d'indiquer son nom sur la ligne de commande. Par exemple, la commande suivante invoque la cible two: |#| bash$ make two |#| Two |#| bash$ Dans cette exemple, make ignore totalement les cibles one et three. >> Invoquer plusieurs cibles Vous pouvez également invoquer plusieurs cibles sur une même ligne de commande: |#| bash$ make three one two |#| Three |#| One |#| Two |#| bash$ Remarquez que les cibles sont traitées dans l'ordre des arguments de la ligne de commande. >> Les cibles par défaut La commande make suppose que la toute première cible rencontrée dans le fichier make est la cible par défault. Considérez l'exemple suivant : |#| bash$ make |#| One |#| bash$ make invoque la première cible rencontrée, one, à la ligne 4 du listing (1.0). Les noms de cible two et three sont considérées comme cibles de remplacement et ne sont pas invoquées. >> Les noms de cible standards Bien qu'aucun standard formel n'existe pour les noms de cibles, vous rencontrerez quelques conventions très répandues lorsque vous examinerez les fichiers make de sources publiquement téléchargeables. [*] _all_ est souvent utilisé comme la première cible et par conséquent celui par défaut. Il sert habituellement à nommer les vrais noms de cibles, qui peuvent être une ou plusieurs autre cibles, qui peuvent être une ou plusieurs autres cibles. En utilisant cette cible, vous construisez le projet entier. [*] _install_ est normalement employé pour installer un projet construit, dans ses répertoires dus système Linux, avec les autorisations correctes et les règles de propriété, en créant les sous-répertoires si nécessaire. [*] _clean_ est souvent utilisé pour vous permettre de supprimer tous les fichiers objet (*.o), les fichiers core, et les autres fichiers provisoires qui ont pu être créés lors de la construction. Cette cible laisse les exécutables et les bibliothèques construits en place. [*] _clobber_ est souvent utilisé pour supprimer les cibles construitent du projet (en général, les exécutables et les bibliothèques). Vous supprimez ces cibles pour reconstruire les fichiers de configuration du projet, etc. [*] _distclean_ est souvent utilisé par les développeurs Linux et GNU pour nettoyer complètement le fichier du projet, sauf en ce qui concerne les fichiers d'origine distribués sur l'Internet. Dépassant clobber, cette cible supprime les fichiers de configuration du projet, etc. >> Les dépendances On accepte couramment des dépendances entre certains noms de cibles. En tant que programmeur( ou progGameur? peut importe;), vous devez savoir que : [*] La cible install dépend habituellement de all. [*] La cible clobber dépend habituellement de clean. [*] La cible distclean dépend habituellement de clobber. Si install dépend de la cible all, le projet est d'abord construit, si nécessaire, puis l'installation tentée. Sinon, il n'existe aucun projet à installer. Cependant, les logiciels sont souvent installés avec les privilèges de root, il n'est par conséquent pas souhaitable de construire votre projet lorsque vous opérez sous root. Si vous vous en sentez capable, vous pouvez omettre cette dépendance dans votre projet. La cible clobber dépend de clean, généralement par commodité. La cible clean s'occupe d'une partie du travail et il vous suffit de définir le reste pour clobber. La cible distclean fonctionne sur le même principe. >> Définir les dépendances Le listing (2.0) présente un nouveau fichier make auquel ont été ajoutées des dépendances. La cible three dépend de la cible two (ligne 10), et la cible two dépend à présent de la cible one (ligne 7). Remarquez que les dépendances sont simplement indiquées après le nom de la cible et le caratère deux-points. Les dépendances supplémentaires sont séparées par un ou plusieurs espaces. Au moins un espace doit séparer le caractère deux points de la prmière dépendances. _= Listing (2.0) Makefile avec dépendances=_ |#| 1: #Source: /home/acidmen/make/RCS/Makefile,v $ |#| 2: |#| 3: |#| 4: one: |#| 5: @echo One |#| 6: |#| 7: two: one |#| 8: @echo Two |#| 9: |#| 10: three: two |#| 11: @echo Three |#| 12: |#| 13: # End Makefile >> Tester les dépendances des cibles A présent, lorsque vous invoquez le fichier make du Listing (2.0) avec le nom de cible three, vous constatez que la commande make traite en réalité les trois cibles: |#| bash$ make three |#| One |#| Two |#| Three |#| bash$ Cet ordre vous surprend-t-il? La cible est exécutée la première, car la cible two dépend d'elle, et la cible three dépend de two. Par conséquent, avant de pouvoir faire quoi que ce soit pour la cible three, ses dépendances doivent être satisfaites. Bien que la cible invoquée soit three, la chaîne des dépendance est tell que make doit invoquer one en premier pour satisfaire les deux autres cibles. >> Définir les macros de make La puissance des scripts shell tient en partie à leur capacité d'utiliser et de manipuler des variables shell et des variables d'environnement. La commande make possède ce genre de fonctionnalité et sait également utiliser les variables d'environnement. Une macro make peut contenir une valeur de chaîne comme un nom de fichier ou une valeur de paramètre. Une macro permet de ne définir qu'une seule fois une certaine valeur dans le Makefile, tout en permettant de l'utiliser dans plusieurs endroits. Lorsqu'une modification est nécessaire, il suffit de modifier l'affectation de la macro, et elle apparaît immédiatement partout où la macro est utilisée. Le listing (3.0) présente une nouvelle édition du Makefile de démonstration. La ligne 5 montre comment déclarer une macro OBJECT et initialiser sa valeur à "Pear". Vous supprimez également les dépendances préalablement utilisées et ajoutez un nom de cible par défaut plus conventionnel, all, à la ligne 7. Remarquez que cette cible dépend de trois cibles, on, two, three. Un projet important est souvent structuré de cette façon dans un fichier make. _= Listing (3.0) Makefile avec une définition de macro=_ |#| 1: # Source: /home/acidmen/make/RCS/Makefile,v $ |#| 2: # Revision: 1.3 $ |#| 3: |#| 4: |#| 5: OBJECT= Pear |#| 6: |#| 7: All: one two three |#| 8: |#| 9: one: |#| 10: @echo one $(OBJECT) |#| 11: |#| 12: two: |#| 13: @echo Two $(OBJECT)s |#| 14: |#| 15: three: |#| 16: @echo Three $(OBJECT)s |#| 17: |#| 18: # End Makefile Les lignes 10, 13 et 16 montrent un exemple d'utilisation de la valeur de la macro. Elle est substituée au signe $ et au nom de la macro qui suit entre parenthèses. Les parenthèses ne sont pas nécessaires lorsque le nom de la macro ne comporte qu'une lettre, par exemple $x. Il est cependant conseillé d'utiliser des noms de macro significatifs. A présent, invoquez ce fichier make et observez son résultat. |#| bash$ make |#| One Pear |#| Two Pears |#| Three Pears |#| bash$ Remarquez la substitution par la valeur. Notez aussi que la cible de la ligne 7 ne comporte aucune instruction. Cette ligne ne sert qu'à relier une cible aux autres dépendances. >> Supplanter les valeurs d'une macro La commande make permet également de passer outre la valeur d'une macro. Avec le fichier make du listing (2.0), vous exécuter : |#| bash$ make OBJECT=apple |#| One apple |#| Two apples |#| Three apples |#| bash$ La ligne 5 affecte la valeur "Pear" à la macro OBJECT, mais l'affectation de la ligne de commande "OBJECT=apple" est prioritaire. On peut donc dire que la commande make effectue d'abord toute les affectations de macros dans le fichier make, puis celles éventuellement rencontrées sur la ligne de commande. >> Utiliser les variables d'environnement La commande make peut également prendre des valeurs de l'environnement. En utilisant une nouvelle fois le fichier make du listing (3.0), voyez si vous pouvez écraser une macro avec une variable d'environnement exportée : |#| bash$ OBJECT=walnut make |#| One Pear |#| Two Pears |#| Three Pears |#| bash$ C'est étonnant, ce test semble ne pas fonctionner, En réalité, les variables d'environnement n'écrasent pas les affectations de macros d'un fichier make. Si la macro OBJECT n'avait pas été définie dans notre fichier make, la substitution aurais été effectuée. Pour preuve, mettez la ligne 5 du listing (3.0) en commentaire et essayez de nouveau. |#| bash$ OBJECT=walnut make |#| One walnut |#| Two walnuts |#| Three walnuts |#| bash$ ` Cette fois, aucune affectation de macro ne figurant dans le Makefile, la valeur de la variable d'environnement OBJECT demeure et est utilisée comme attendu. _= Listing (4.0) Makefile avec l'affectation de la macro mise en commentaire=_ |#| 1: # Source: /home/acidmen/make/RCS/Makefile,v $ |#| 2: # Revision: 1.3 $ |#| 3: |#| 4: |#| 5: #OBJECT= Pear |#| 6: |#| 7: All: one two three |#| 8: |#| 9: one: |#| 10: @echo one $(OBJECT) |#| 11: |#| 12: two: |#| 13: @echo Two $(OBJECT)s |#| 14: |#| 15: three: |#| 16: @echo Three $(OBJECT)s |#| 17: |#| 18: # End Makefile >> Utiliser le paramètre -e Un paramètre de make, -e, permet à l'environnement d'écraser les macros du fichier make. Toutefois, utilisez-le prudemment, car l'environnement peut être modifié bien plus que vous ne le pensez. En reprenant le fichier make d'origine du listing vous pouvez tester cette règle : |#| bash$ OBJECT=walnut make |#| One Pear |#| Two Pears |#| Three Pears |#| bash$ OBJECT=walnut make -e |#| One walnut |#| Two walnuts |#| Three walnuts |#| bash$ - Vous avez exporté la variable d'environnement OBJECT et exécuté la commande make, mais la variable OBJECT n'a aucun effet - Vous avez exportez la variable d'environnement OBJECT une nouvelle fois, mais exécutez ici make avec le paramètre -e. Cette fois, la variable d'environnement OBJECT influence le résultat. ++ Lorsque vous comptez sur des variables d'environnement dans un fichier make, assurez-vous qu'elles sont bien exportées de votre shell. Assurez-vous également de bien comprendre ce que la commande d'exportation intégrée effectue dans le shell bash. >> Les suffixe de fichiers La commande make est très largement dépendante des suffixes de fichiers, desquels elle déduit le type du fichier. Grâce à des conventions, elle peut appliquer des règles de déduction par défaut. Avant d'étudier ces règles, regardons quelques suffixes connus de make, dans le tableau (1.0) Tableau (1.0) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > Suffixe de fichier < | > Description < - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ] .c | Module source en langage C [ ] .h | Fichier d'en-tête en lengage C [ ] .cc | Module source en langage C++ [ ] .o | Fichier objet compilé [ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >> Comprendre les règles de déduction La puissance de la commande make propose en partie sur ces règes de déductions. Mais ceux-ci peuvent être une source de frustration lorsqu'elles ne sont pas bien comprises.( exemple tiré d'un fait vécue : essayer de sodomiser sa boite à malle ) =| /---|--- 8=====D|______| || || || (c) Walt Disney > Tester les règles de déduction par défault Ecrivez ce ptit programme - hello.c - |#| 1: #include |#| 2: int main(int argc, char **argv) { |#| 3: |#| 4: (void) argc; |#| 5: (void) argv; |#| 6: |#| 7: PUTS("Hello World!"); |#| 8: return 0; |#| 9: } /\ Pause publicitaire payée en nature par JoJo savard / \ Qui vous dit : < > 1-800-Dit-moi-pas? \ / "salut mes chouuuxx mieux ruiner c'est mieux que vaut l'astre \/ est dans la lune, la lune est dans le ciel suivez votre bonne étoile." hooo, un beau message d'espoir pour le temps des fêtes ;) mon coeur se contient plus. ... qqn est partant pour coder a ze power di-kRypt3rz 3000 for j0j0 s4vardz? Bon, une fois votre programme écrit, testez ensuite les règles de déductions par défault de make, comment indiqué dans le listing (4.5). _= Listing (4.5) Tester les règles de déduction de hello.o de GNU make=_ |#| 1: bash$ mkdir experiment |#| 2: bash$ cd experiment |#| 3: bash$ cp ../hello.c . |#| 4: bash$ >Makefile |#| 5: bash$ make hello.o |#| 6: cc -c hello.c -o hello.o |#| 7: bash$ type cc |#| 8: cc is /usr/bin/cc |#| 9: bash$ls -dl /usr/bin/cc |#| 10: lrwxrwxrwx 1 root root 3 Dec 15 2002 /usr/bin/cc -> gcc |#| 11: bash$ Voici les étapes de ce test : 1. Vous créez un répertoire dans lequel travailler ( ligne 1 ) 2. Vous passez dans ce répertoire ( ligne 2 ) 3. Vous recopiez votre programme hello.c dans le répertoire en cours (ligne 3). Remarquez que le répertoire source de hello.c peut être différent dans votre cas. 4. Vous créez un Makefile vide dans lequel make pourrat travailler ( ligne 4 ). 5. Vous invoquez make avec le Makefile vide dans le répertoire en cours (ligne 5) Indiquez explicitement le nom de cible hello.o à make, car le Makefile ne contient aucune cible définie. 6. La commande make s'exécute, montrant que sa règle interne déduit que hello.c doit être compilé par un compilateur C pour produire hello.o ( ligne 6 ). 7. Vous demandez au shell d'où provient cc ( ligne 7 ). Le shell indique le nom du chemin à la ligne 8. 8. Vous invoquez la commande ls avec le paramètre -dl ( ligne 9 ) et découvrez que la commande cc est un lien symbolique vers la commande gcc ( ligne 10 ). Indépendamment du fait que la règle ne fournit pas les paramètres -D_GNU_SOURCE et -Wall, tout se passe très bien sans aucune aide de votre part. Ces résultats pouvant être légèrement différents de ceux attendus, vous devez définir explicitement une règle correspondant à vos besoins. >> Définir une règle de déduction Le listing (5.0) présente un fichier make de démonstration contenant une règle de déduction. _= Listing (5.0) Une règle de déduction de make =_ |#| 1: CC = gcc |#| 2: STD = _GNU_SOURCE |#| 3: |#| 4: .c.o: |#| 5: $(CC) -c -Wall $(CFLAGS) -D$(STD) $< -o $@ La ligne 1 montre la façon conventionnelle de choisir votre compilateur dans un fichier make, avec la macro CC. Ici, la macro pointe directement sur le compilateur gcc. La ligne 2 définit la macro STD permettant de sélectionner le standard sous lequel le compiler. La cible de la ligne 4 est d'un type spécial: elle est composée du suffixe du fichier d'origine et de celui du fichier cible. En définissant une cible sous cette forme, vous définissez en réalité une règle de déduction destinée à make. ^ / \ / \ / A \ ttention ------------------------------------------------------------ /_ _ _ _\ / Ne définissez jamais de dépendance pour une règle de déduction. La / commande make ignore les dépendances des règles de déduction. Sur /__ _ certaines plates-formes UNIX, la règle n'est pas interprétée comme une règle de déduction lorsque des dépendances sont indiquées --------------------------------------------------------------------- La ligne 4 définit une règle de déduction indiquant que si vous dispoez d'une cible constituée d'un fichier objet (*.o) et constatez que vous disposez du fichier d'entrée correspondant avec le suffixe .c, vous exécutez les instructions qui suivent. >> Instruction à exécuter La ligne 5 du listing (5.0) représente la seule instruction à exécuter. Une instruction doit commencer par un caractère de tabulation. Les espaces ici sont interdites. Il peut y avoir plusieurs actions, mais une seule est nécessaire. Notez également que si la commande de l'action commence par un caractère "@", make supprime l'affichage de la commande sur la sortie standard. Vous l'avez peut-être remarqué aux lignes 5, 8 et 11 du listing (2.0) ____ || |\ || ||=== | _ | || | \|| ||= ||_| | || | \ | || |____|_ _ _ _ _ _ _ _ _ _ _ _ _ _ __ _ _ ______ _ _ _ _ _ _ _ __ Les lignes d'action d'un fichier make doivent commencer par une tabulation. Certains éditeurs insistent sur l'utilisation des blancs lorsque vous appuyez sur la touche Tab. Vérifiez que le premier caratère est bien le caractère ASCII de tabulation, sinon make ne l'accepte pas comme instruction à exécuter. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --- -- - - -- -- La ligne 5 du listing (5.0) décrit une instruction de compilation faisant grand usage des macros. La plupart sont aisément compréhensibles. Cela nous laisse deux macros d'aspect bizzare : "$<" et "$@". >>Les macros intégrées La commande make gère au moins six macros intégrées. Celles-ci sont différentes des macros définies par l'utilisateur, car elles n'ont jamais besoin d'être définies. Elles sont gérées en interne par la commande make et leur valeur change en fonction des règles de déduction. Les plus importantes de ces macros sont présentées au Tableau (2.0) Tableau (2.0) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > Nom de macro < | > Description < - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ] $* | Cette macro fournit l'élément nom de fichier du [ ] | dépendant actuel ( le suffixe étant supprimer [ ] | [ ] $@ | Cette macro fournit le nom complet, avec le [ ] | suffixe de la cible actuelle. [ ] | [ ] $< | Cette macro fournit le nom de fichier dépendant [ ] | complet, avec le suffixe. [ ] | [ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dans la session étudiée au listning (4.5), la ligne 5 désigne hello.o comme cible, Dans la règle de déduction des lignes 5 et 6 du listing (5.0), les valeurs du Tableau (3.0) ci-dessous devraient être indiquées. Tableau (3.0) - - - - - - - - - - - - - - - - - - - - - - - - > Nom de macro < | > valeur de la macro < - - - - - - - - - - - - - - - - - - - - - - - - ] $* | "hello" [ ] $@ | "hello.o" [ ] $< | "hello.c" [ - - - - - - - - - - - - - - - - - - - - - - - - Souvez-vous que ces macros intégrées ne possèdent de valeur qu'à l'intérieur d'une règle de déduction. La macro $< comporte une petite exception, et au moins deux autre macros intégrées ont d'autres usages. >> La macro CFLAGS La macro CFLAGS est utilisée pour contenir d'autres paramètres du compilateur C. Si elle n'est pas définie dans le fichier make, vous avez la possibilité d'initialiser une variable d'environnement pour la session dec compilation en cours. > Utiliser CFLAGS à partir du shell Une habitude courante consiste à utiliser la macro CFLAGS pour effectuer ( ou non ) une compilation en mode débogage. Notez le déroulement de cette procédure dans cette exemple : |#| bash$ rm hello.o |#| export CFLAGS=-g |#| bash$ make hello.o |#| gcc -c -Wall -g -D_GNU_SOURCE hello.c -o hello.o |#| bash$ Voici la procédure utilisée ici : 1. Vous prenez soin de vous débarrasser du dernier hello.o créé ( sinon, make ne se sentira pas obligé de le recréer ) 2. Vous définissez et exportez la variable d'environnement CFLAGS avec le paramètre de mise au point de gcc -g. 3. A partir de maintenant, lorsque vous compilez, la commande make prend automatiquement la valeur exportée de CFLAGS et l'applique à la ligne de commande ( Remarquez que le paramètre -g apparaît dans la commande gcc ). On peut utiliser la même technique pour recompiler afin d'optimiser au lieu de déboguer. Ce petit truc s'avère particulièrement utile lorsque le Makefile se trouve contrôlé par un RCS ; il n'est pas nécessaire de le modifier. Joyeux noël : ut2k3 : lyr22-rz743-a9d7t-cnnen : ___ ___ .----/ /----------- ---------- --------- - -- - - --- -----------/ / | / / / /| : / / / / : | / / acidmen@mindkind.org / / | |/ / / / | /___/-- ---------- ----- ----------------- - ------- -- --- -/___/----'