dev:makefile
no way to compare when less than two revisions
Différences
Ci-dessous, les différences entre deux révisions de la page.
— | dev:makefile [2018/11/17 12:53] (Version actuelle) – créée - modification externe 127.0.0.1 | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
+ | ====== makefile, comprendre le makefile ====== | ||
+ | On ne va pas chercher à faire des Makefile qui " | ||
+ | |||
+ | |||
+ | |||
+ | ===== Plan ===== | ||
+ | |||
+ | * Généralités | ||
+ | |||
+ | * Intérêt | ||
+ | |||
+ | * Structure générale d'un Makefile | ||
+ | |||
+ | * Utilisation d'un Makefile | ||
+ | |||
+ | * Ordre de compilation | ||
+ | |||
+ | * Makefile & C/C++ | ||
+ | |||
+ | * Un seul fichier source | ||
+ | |||
+ | * Plusieurs fichiers sources | ||
+ | |||
+ | * Compilateurs & Flags | ||
+ | |||
+ | * Une règle pour tout | ||
+ | |||
+ | * Création automatique des dépendances | ||
+ | |||
+ | * Quelques règles de bonne conduite | ||
+ | |||
+ | * Makefile & LaTeX | ||
+ | |||
+ | * Un petit mot sur LaTeX | ||
+ | |||
+ | * Les variables | ||
+ | |||
+ | * Un seul fichier source | ||
+ | |||
+ | * Plusieurs fichiers sources | ||
+ | |||
+ | * Makefile & Java | ||
+ | |||
+ | * Un petit mot sur Java | ||
+ | |||
+ | * Un seul fichier source | ||
+ | |||
+ | * Plusieurs fichiers sources & pas de package | ||
+ | |||
+ | * Plusieurs fichiers sources & package | ||
+ | |||
+ | * La notion de package | ||
+ | |||
+ | * L' | ||
+ | |||
+ | * Le Makefile propre à chaque package | ||
+ | |||
+ | * Le Makefile racine | ||
+ | |||
+ | * Le Makefile.include | ||
+ | |||
+ | * Conclusion & Ressources | ||
+ | |||
+ | |||
+ | |||
+ | ===== Généralités ===== | ||
+ | |||
+ | ==== Intérêt ==== | ||
+ | |||
+ | Le Makefile est un utilitaire écrit en version GNU par Richard Stallman et Roland McGrath. Typiquement, | ||
+ | |||
+ | |||
+ | |||
+ | Par comparaison de date de création/ | ||
+ | |||
+ | |||
+ | |||
+ | Structure générale d'un Makefile | ||
+ | |||
+ | Un Makefile reste avant tout un fichier texte appelé " | ||
+ | |||
+ | |||
+ | |||
+ | Typiquement, | ||
+ | |||
+ | |||
+ | |||
+ | * La définition des variables. Il s'agit de définir les valeurs pour les différentes variables à utiliser. Exemple : | ||
+ | |||
+ | |||
+ | |||
+ | < | ||
+ | |||
+ | SRCS = main.c file1.c file2.c | ||
+ | |||
+ | CC = gcc</ | ||
+ | |||
+ | |||
+ | |||
+ | Pour expliquer, CC sera simplement une variable qui va contenir le compilateur (gcc, dans notre cas), SRCS est une variable qui va contenir tous les fichiers sources et CFLAGS permet de gérer les flags nécessaires à la compilation. | ||
+ | |||
+ | |||
+ | |||
+ | Notons que, par convention, les noms de variables seront tjrs en majuscule. | ||
+ | |||
+ | |||
+ | |||
+ | Par la suite, pour accéder à la valeur d'une variable, il suffit de faire: | ||
+ | |||
+ | |||
+ | |||
+ | $(NOM_DE_LA_VARIABLE) | ||
+ | |||
+ | |||
+ | |||
+ | Simplement, il faut mettre le nom de la variable entre parenthèses, | ||
+ | |||
+ | |||
+ | |||
+ | * Les règles. Ces lignes définissent sous quelles conditions un fichier donné a besoin d' | ||
+ | |||
+ | |||
+ | < | ||
+ | main.o: main.c | ||
+ | |||
+ | $(CC) $(CFLAGS) -c main.c | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Important** : la deuxième ligne ($(CC)...) doit nécessairement commencé par une tabulation. Makefile est très pointilleux sur les aspects syntaxiques. | ||
+ | |||
+ | |||
+ | |||
+ | La première ligne donne la règle. La deuxième, l' | ||
+ | |||
+ | |||
+ | |||
+ | Cela signifie simplement que le fichier ' | ||
+ | |||
+ | |||
+ | |||
+ | * Les commentaires. Comme tout script ou programme, un Makefile peut contenir des commentaires. Une ligne sera commentée si elle commence par le caractère: | ||
+ | |||
+ | |||
+ | |||
+ | # | ||
+ | |||
+ | |||
+ | |||
+ | ==== Utilisation d'un Makefile ==== | ||
+ | |||
+ | On exécute un Makefile en tapant simplement la commande (dans une Konsole/ | ||
+ | |||
+ | |||
+ | |||
+ | make | ||
+ | |||
+ | |||
+ | |||
+ | On peut aussi faire en sorte d' | ||
+ | |||
+ | |||
+ | |||
+ | make nom_de_la_règle | ||
+ | |||
+ | |||
+ | |||
+ | ==== Ordre de compilation ==== | ||
+ | |||
+ | Quand un Makefile est exécuté, on appelle la commande make pour exécuter une cible (' | ||
+ | |||
+ | |||
+ | |||
+ | Une cible peut être le nom du fichier à créer ou tout simplement un nom qcq utilisé comme point de départ. | ||
+ | |||
+ | |||
+ | |||
+ | Quand make est invoqué, il évalue d' | ||
+ | |||
+ | |||
+ | |||
+ | Je passe sur les détails de gestion de dépendance. C'est donné au lecteur à titre d' | ||
+ | |||
+ | |||
+ | |||
+ | ===== Makefile & C/C++ ===== | ||
+ | |||
+ | ==== Un seul fichier source ==== | ||
+ | |||
+ | Considérons un exemple simple de Makefile qui sera utilisé dans le cadre d'un programme ne nécessitant qu'un seul fichier source | ||
+ | |||
+ | < | ||
+ | |||
+ | # Règle de haut niveau pour créer le programme. | ||
+ | |||
+ | all: main | ||
+ | |||
+ | | ||
+ | |||
+ | # Compilation du fichier source. | ||
+ | |||
+ | main.o: main.c | ||
+ | |||
+ | gcc -g -Wall -c main.c | ||
+ | |||
+ | | ||
+ | |||
+ | # " | ||
+ | |||
+ | main: main.o | ||
+ | |||
+ | gcc -g main.o -o main | ||
+ | |||
+ | | ||
+ | |||
+ | # Nettoyage. | ||
+ | |||
+ | clean: | ||
+ | |||
+ | / | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Quelques indications :** | ||
+ | |||
+ | * Toutes les règles ne seront pas invoquées lors de l' | ||
+ | |||
+ | |||
+ | |||
+ | make clean | ||
+ | |||
+ | |||
+ | |||
+ | * Une règle ne doit pas avoir nécessairement de dépendances. Si il n'y a pas de dépendances, | ||
+ | |||
+ | * Une règle ne doit pas nécessairement avoir de commandes. Par exemple, la règle all n'a pas de commandes propres. Elle sert seulement à appeler les autres règles. En fait, ça permet de s' | ||
+ | |||
+ | * Utiliser le chemin complet pour ' | ||
+ | |||
+ | |||
+ | |||
+ | ==== Plusieurs fichiers sources ==== | ||
+ | |||
+ | La plupart du tps, tout projet de programmation comportera plus d'un fichier source. C'est vraiment dans ce genre de cas que l' | ||
+ | |||
+ | |||
+ | < | ||
+ | # Règle de haut niveau pour créer le programme. | ||
+ | |||
+ | all: prog | ||
+ | |||
+ | | ||
+ | |||
+ | # Le programme est fait de plusieurs fichiers sources. | ||
+ | |||
+ | prog: main.o file1.o file2.o | ||
+ | |||
+ | gcc main.o file1.o file2.o -o prog | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour main.o | ||
+ | |||
+ | main.o: main.c file1.h file2.h | ||
+ | |||
+ | gcc -g -Wall -c main.c | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour file1.o | ||
+ | |||
+ | file1.o: file1.c file1.h | ||
+ | |||
+ | gcc -g -Wall -c file1.c | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour file2.o | ||
+ | |||
+ | file2.o: file2.c file2.h | ||
+ | |||
+ | gcc -g -Wall -c file2.c | ||
+ | |||
+ | | ||
+ | |||
+ | # Nettoyage. | ||
+ | |||
+ | clean: | ||
+ | |||
+ | / | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Quelques explications :** | ||
+ | |||
+ | * Il y a une seule règle par fichier. C'est qq peu redondant. On verra par la suite comment on peut faire pour agréger tout cela. | ||
+ | |||
+ | * On ajoute des dépendances pour les headers (fichiers *.h). Si un de ces fichiers changent, le fichier qui les inclut doit aussi être recompilé. Ce n'est certes pas trjs vrai mais il est préférable de compiler un peu trop afin d' | ||
+ | |||
+ | |||
+ | |||
+ | ==== Compilateurs & Flags ==== | ||
+ | |||
+ | Comme on peut le voir dans les segments de code supra, il y a pas mal de pattern redondant dans les règles de notre Makefile. | ||
+ | |||
+ | |||
+ | |||
+ | Ca risque de poser problème qd on voudra faire des changements, | ||
+ | |||
+ | |||
+ | |||
+ | Une solution relativement simple à ce problème est d' | ||
+ | |||
+ | |||
+ | |||
+ | Ca nous donnerait un Makefile de la forme suivante : | ||
+ | |||
+ | < | ||
+ | |||
+ | # Utilisation de gcc pour compiler les sources. | ||
+ | |||
+ | CC = gcc | ||
+ | |||
+ | # Le linker est aussi gcc. Mais il pourrait être différent du compilateur, | ||
+ | |||
+ | LD = gcc | ||
+ | |||
+ | # Les flags du compilateurs. | ||
+ | |||
+ | CFLAGS = -g -Wall | ||
+ | |||
+ | # Les flags du linker. | ||
+ | |||
+ | LDFLAGS = | ||
+ | |||
+ | # rm. C'est bien d'en faire une variable, car son emplacement peut varier d'une machine (ou distribution) à l' | ||
+ | |||
+ | RM = /bin/rm -f | ||
+ | |||
+ | # La liste des fichiers objet à générer. | ||
+ | |||
+ | OBJS = main.o file1.o file2.o | ||
+ | |||
+ | # Le nom de l' | ||
+ | |||
+ | PROG = prog | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle de haut niveau pour créer le programme | ||
+ | |||
+ | all: $(PROG) | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour linker le programme | ||
+ | |||
+ | $(PROG): $(OBJS) | ||
+ | |||
+ | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour main.o | ||
+ | |||
+ | main.o: main.c file1.h file2.h | ||
+ | |||
+ | $(CC) $(CFLAGS) -c main.c | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour file1.o . | ||
+ | |||
+ | file1.o: file1.c file1.h | ||
+ | |||
+ | $(CC) $(CFLAGS) -c file1.c | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour file2.o. | ||
+ | |||
+ | file2.o: file2.c file2.h | ||
+ | |||
+ | $(CC) $(CFLAGS) -c file2.c | ||
+ | |||
+ | | ||
+ | |||
+ | # Nettoyage. | ||
+ | |||
+ | clean: | ||
+ | |||
+ | $(RM) $(PROG) $(OBJS) | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Quelques explications :** | ||
+ | |||
+ | * Typiquement le nom des variables est tjrs en majuscule. Pour accéder à la valeur d'une variable, il suffit de la mettre entre parenthèses précédées d'un $ | ||
+ | |||
+ | * On définit énormément de variables. Ca rend les modifications plus facile car il y a un seul endroit à changer. | ||
+ | |||
+ | * On a encore un problème. On définit une règle pour chaque fichier source. C'est redondant. Ca ne pose pas trop de problèmes si il y a 3-4 fichiers sources. C'est un gros problème qd il y en a une bonne dizaine (ce qui est le cas dans les projets de moyenne envergure). | ||
+ | |||
+ | |||
+ | |||
+ | ==== Une règle pour tout ==== | ||
+ | |||
+ | La phase suivante est d' | ||
+ | |||
+ | < | ||
+ | |||
+ | # Je passe toutes les définitions de variables. | ||
+ | |||
+ | | ||
+ | |||
+ | # La règle de linkage, identique à ce qui a été proposé supra. | ||
+ | |||
+ | $(PROG): $(OBJS) | ||
+ | |||
+ | $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) | ||
+ | |||
+ | | ||
+ | |||
+ | # Une ' | ||
+ | |||
+ | %.o: %.c | ||
+ | |||
+ | $(CC) $(CFLAGS) -c $< | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Quelques explications :** | ||
+ | |||
+ | * Le caractère ' | ||
+ | |||
+ | |||
+ | |||
+ | %.o: %.c | ||
+ | |||
+ | |||
+ | |||
+ | Signifie que "un fichier avec un suffixe ' | ||
+ | |||
+ | * le " | ||
+ | |||
+ | |||
+ | |||
+ | ==== Création automatique des dépendances ==== | ||
+ | |||
+ | Un des problèmes avec l' | ||
+ | |||
+ | |||
+ | |||
+ | On peut s' | ||
+ | |||
+ | |||
+ | |||
+ | Je propose de jeter un coup d'oeil à makedepend et son utilisation dans un Makefile. | ||
+ | |||
+ | |||
+ | < | ||
+ | # La liste des fichiers source. | ||
+ | |||
+ | SRCS = main.c file1.c file2.c | ||
+ | |||
+ | . | ||
+ | |||
+ | . | ||
+ | |||
+ | # Le reste du Makefile est identique | ||
+ | |||
+ | # A la fin, on ajoute " | ||
+ | |||
+ | | ||
+ | |||
+ | # Règle pour construire la liste des dépendances et l' | ||
+ | |||
+ | depend: | ||
+ | |||
+ | $(RM) .depend | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | # Il suffit ensuite d' | ||
+ | |||
+ | include .depend | ||
+ | |||
+ | |||
+ | </ | ||
+ | **Quelques explications :** | ||
+ | |||
+ | * L' | ||
+ | |||
+ | |||
+ | |||
+ | make depend | ||
+ | |||
+ | |||
+ | |||
+ | Va permettre d' | ||
+ | |||
+ | * On inclut ensuite les dépendances dans le Makefile via | ||
+ | |||
+ | |||
+ | |||
+ | include .depend | ||
+ | |||
+ | |||
+ | |||
+ | Ces dépendances seront vérifiées automatiquement à chaque compilation. Il faut donc faire le "make depend" | ||
+ | |||
+ | |||
+ | |||
+ | ==== Quelques règles de bonne conduite ==== | ||
+ | |||
+ | * Il est tjrs bien d' | ||
+ | |||
+ | * Cette règle de nettoyage peut avoir plusieurs niveaux (juste supprimer les fichier *~, supprimer les fichiers temporaires, | ||
+ | |||
+ | * Il est intéressant que les fichiers sources, objets et binaires ne se trouvent pas dans le(s) même(s) répertoire(s). Penser à une règle de type " | ||
+ | |||
+ | |||
+ | |||
+ | ===== Makefile & LaTeX ===== | ||
+ | |||
+ | ==== Un petit mot sur LaTeX ==== | ||
+ | |||
+ | LaTeX est un système typographique de haute qualité, développé dans les 70's (si je ne m' | ||
+ | |||
+ | |||
+ | |||
+ | L' | ||
+ | |||
+ | |||
+ | |||
+ | LaTeX n'est pas, a priori, WYSIWYG (what you see is what you get). Il fonctionne par déclaration d' | ||
+ | |||
+ | |||
+ | |||
+ | Ceux qui sont intéressés par LaTeX peuvent consulter (gratuitement) le document intitulé Une courte (?) introduction à LaTeX disponible ici | ||
+ | |||
+ | |||
+ | |||
+ | Pour la suite de cette section, je suppose que vous savez comment fonctionne LaTeX (compilation, | ||
+ | |||
+ | |||
+ | |||
+ | ==== Les variables ==== | ||
+ | |||
+ | A ce stade du tuto, vous devez avoir compris qu'il est intéressant (obligatoire? | ||
+ | |||
+ | |||
+ | |||
+ | Cette section a pour but de proposer qq variables qui me semblent pertinentes dans le cadre d'un Makefile ayant pour but la compilation d'un projet LaTeX. | ||
+ | |||
+ | |||
+ | |||
+ | Voici ce que je propose : | ||
+ | |||
+ | |||
+ | < | ||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | # Commands | ||
+ | |||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | LATEX = latex | ||
+ | |||
+ | BIBTEX | ||
+ | |||
+ | DVIPS = dvips | ||
+ | |||
+ | DVIPS_OPTION | ||
+ | |||
+ | PDFLATEX | ||
+ | |||
+ | DVIPDF | ||
+ | |||
+ | DVIPDF_OPTION | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Une petite explication :** | ||
+ | |||
+ | * J'ai appelé ces variables ' | ||
+ | |||
+ | * DVIPS_OPTION reprend les options de la commande divps. Expliquer l' | ||
+ | |||
+ | * DVIPDF_OPTION reprend les options de la commande dvipdf. Idem que pour DVIPS_OPTION. | ||
+ | |||
+ | |||
+ | |||
+ | Les autres variables à définir sont les suivantes : | ||
+ | |||
+ | < | ||
+ | |||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | # LaTeX files # | ||
+ | |||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | TARGET = | ||
+ | |||
+ | | ||
+ | |||
+ | BIBSRC = | ||
+ | |||
+ | | ||
+ | |||
+ | TEXSRC = | ||
+ | |||
+ | | ||
+ | |||
+ | PICTURES = \ | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | PDFTARGET = $(TARGET).dvi | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * PICTURES est la variable qui contient le chemin vers les figures (.eps) à insérer dans le document LaTeX. | ||
+ | |||
+ | * TARGET va contenir le nom du fichier ' | ||
+ | |||
+ | * TEXSRC contiendra le(s) fichier(s) source du projet LaTeX. Ce sera à définir en fonction du type de projet (un seul fichier ou plusieurs) | ||
+ | |||
+ | * BIBSRC contiendra le(s) fichier(s) de bibliographie. A noter que gérer plusieurs fichiers de bibliographie est assez lourd en LaTeX. Il existe des packages permettant cela mais ça devient moins intuitif, surtout au niveau de la compilation. Comme l' | ||
+ | |||
+ | * PDFTARGET va être notre variable principale... | ||
+ | |||
+ | |||
+ | |||
+ | Il est aussi intéressant de définir des fichiers de log, qui contiendront la trace de la compilation. | ||
+ | < | ||
+ | |||
+ | |||
+ | # -------------------------------------------------------------------------- # | ||
+ | |||
+ | # Log files # | ||
+ | |||
+ | # -------------------------------------------------------------------------- # | ||
+ | |||
+ | | ||
+ | |||
+ | LOG = compile.log | ||
+ | |||
+ | PDFLOG = compilepdf.log | ||
+ | |||
+ | | ||
+ | |||
+ | LOGFILE = $(LOG) $(PDFLOG) | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * LOG va contenir les infos concernant la compilation LaTeX proprement dite. | ||
+ | |||
+ | * PDFLOG va se contenter de résumer la transformation du dvi en pdf. | ||
+ | |||
+ | * LOGFILE est une sorte de variable globale, qui contient tous les types de fichiers de log. Ca sera utile pour le nettoyage du répertoire. | ||
+ | |||
+ | |||
+ | |||
+ | ==== Un seul fichier source ==== | ||
+ | |||
+ | Certains petits projets LaTeX nécessitent un seul fichier source. Pour simplifier, je vais aussi considérer qu'il n'y a pas de fichier de bibliographie. Ce cas sera abordé dans la section suivante. | ||
+ | |||
+ | |||
+ | |||
+ | La première chose à faire, c'est de compléter les variables liées au(x) fichier(s) LaTeX : | ||
+ | |||
+ | < | ||
+ | |||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | # LaTeX files # | ||
+ | |||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | TARGET = target | ||
+ | |||
+ | | ||
+ | |||
+ | TEXSRC = monFichier.tex | ||
+ | |||
+ | | ||
+ | |||
+ | PICTURES = \ | ||
+ | |||
+ | Pictures/ \ | ||
+ | |||
+ | | ||
+ | |||
+ | PDFTARGET = $(TARGET).dvi | ||
+ | </ | ||
+ | |||
+ | |||
+ | Rien de bien surprenant, jusqu' | ||
+ | |||
+ | |||
+ | |||
+ | Passons maintenant en revue les règles... | ||
+ | |||
+ | |||
+ | |||
+ | default: $(PDFTARGET) | ||
+ | |||
+ | |||
+ | |||
+ | Il s'agit ici de la règle par défaut, celle qui sera appliquée 'par défaut' | ||
+ | |||
+ | |||
+ | |||
+ | A noter que cette règle ne contient aucune commande à exécuter. Elle fait simplement un renvoi à la règle qui gère $(TARGET).dvi. | ||
+ | |||
+ | |||
+ | |||
+ | Cette règle est la suivante : | ||
+ | |||
+ | < | ||
+ | |||
+ | # makes the dvi output file | ||
+ | |||
+ | $(TARGET).dvi: | ||
+ | |||
+ | @echo | ||
+ | |||
+ | @echo \* | ||
+ | |||
+ | @echo \* Compiling $(TARGET) - compilation log in $(LOG)... | ||
+ | |||
+ | @echo \* | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | echo '** Re-running LaTeX **'; \ | ||
+ | |||
+ | | ||
+ | |||
+ | done | ||
+ | |||
+ | | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * les premières commandes (@echo) permettent d' | ||
+ | |||
+ | * La commande $(LATEX) compile notre fichier LaTeX. On remarque qu'on ne place pas le résultat dans un fichier de LOG. La raison est simple: en cas de problème lors de la compilation, | ||
+ | |||
+ | * On fait ensuite une boucle, pour recompiler le fichier LaTeX tant qu'il y a des références qui sont indéfinies. Les habitués de LateX savent que, lq il y a des renvois dans le txt (\ref{}), il faut compiler plusieurs fois pq l' | ||
+ | |||
+ | * La dernière commande fait un renvoi à la règle permettant de transformer le fichier .tex en .pdf. L' | ||
+ | |||
+ | |||
+ | |||
+ | Cette règle pour le pdf a la forme suivante : | ||
+ | |||
+ | |||
+ | < | ||
+ | # makes the pdf output file | ||
+ | |||
+ | $(TARGET).pdf: | ||
+ | |||
+ | @echo | ||
+ | |||
+ | @echo \* | ||
+ | |||
+ | @echo \* Running pdfLaTeX $(TARGET) | ||
+ | |||
+ | @echo \* | ||
+ | |||
+ | | ||
+ | |||
+ | </ | ||
+ | |||
+ | Rien de bien particulier. On suit simplement la commande dvipdf (cfr. man page pour ceux qui ne connaissent pas). | ||
+ | |||
+ | |||
+ | |||
+ | Comme d' | ||
+ | |||
+ | |||
+ | |||
+ | make clean | ||
+ | |||
+ | |||
+ | |||
+ | Cette règle aura la forme suivante: | ||
+ | |||
+ | |||
+ | < | ||
+ | # clean the current directory | ||
+ | |||
+ | clean: | ||
+ | |||
+ | rm -f *~ | ||
+ | |||
+ | rm -f $(TEXSRC: | ||
+ | |||
+ | rm -f $(TEXSRC: | ||
+ | |||
+ | rm -f $(TEXSRC: | ||
+ | |||
+ | rm -f $(TARGET).log $(TEXSRC: | ||
+ | |||
+ | rm -f $(TARGET).lof $(TARGET).lot $(TARGET).toc | ||
+ | |||
+ | rm -f $(TARGET).bbl $(TARGET).blg $(TARGET).out | ||
+ | |||
+ | rm -f $(LOGFILE) | ||
+ | |||
+ | clear | ||
+ | |||
+ | <: | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * On supprime les fichiers temporaires créés par Emacs (pour ceux qui l' | ||
+ | |||
+ | * les actions de la forme | ||
+ | |||
+ | |||
+ | |||
+ | rm -f $(TEXSRC: | ||
+ | |||
+ | |||
+ | |||
+ | est relativement simple à comprendre. Simplement, on remplace l' | ||
+ | |||
+ | * On remarque que la règle clean supprime tous les fichiers temporaires propres à LaTeX. | ||
+ | |||
+ | |||
+ | |||
+ | ==== Plusieurs fichiers sources ==== | ||
+ | |||
+ | Dans d' | ||
+ | |||
+ | |||
+ | |||
+ | Again, il nous faut définir des variables propres à notre projet LaTeX | ||
+ | |||
+ | < | ||
+ | |||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | # LaTeX files # | ||
+ | |||
+ | # --------------------------------------------------------------------------- # | ||
+ | |||
+ | TARGET = StateOfTheArt | ||
+ | |||
+ | | ||
+ | |||
+ | BIBSRC = Bibliography.bib | ||
+ | |||
+ | | ||
+ | |||
+ | TEXSRC = \ | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | PICTURES = \ | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * Supposons que notre document soit constitué d'un abstract (i.e. résumé), de 7 chapitres et une bibliographie. | ||
+ | |||
+ | * Pour des raisons de lisibilité, | ||
+ | |||
+ | * A noter qu'on peut facilement réduire cette variable via l' | ||
+ | |||
+ | |||
+ | |||
+ | TEXSRC = $(wildcard *.tex) | ||
+ | |||
+ | |||
+ | |||
+ | Si on gagne en espace, je trouve qu'on perd qq peu en lisibilité, | ||
+ | |||
+ | |||
+ | |||
+ | Passons maintenant aux règles... La règle par défaut sera: | ||
+ | |||
+ | |||
+ | |||
+ | default: $(PDFTARGET) | ||
+ | |||
+ | |||
+ | |||
+ | soit totalement identique à celle de la section précédente... | ||
+ | |||
+ | |||
+ | |||
+ | Pour créer le .dvi, la règle sera plus complexe que dans la précédente section... | ||
+ | |||
+ | < | ||
+ | |||
+ | # makes the dvi output file | ||
+ | |||
+ | $(TARGET).dvi: | ||
+ | |||
+ | @echo | ||
+ | |||
+ | @echo \* | ||
+ | |||
+ | @echo \* Compiling $(TARGET) - compilation log in $(LOG) ... | ||
+ | |||
+ | @echo \* | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | echo '** Re-running LaTeX **'; \ | ||
+ | |||
+ | | ||
+ | |||
+ | done | ||
+ | |||
+ | | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * La différence se situe au niveau de la 4ème ligne... On commence par appeler la règle qui permet de gérer le fichier de bibliographie | ||
+ | |||
+ | * Le reste est identique. | ||
+ | |||
+ | |||
+ | |||
+ | Cette règle à la forme suivante: | ||
+ | |||
+ | |||
+ | < | ||
+ | # runs bibtex | ||
+ | |||
+ | $(TARGET).bbl: | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | clear | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * Cette règle sera exécutée si | ||
+ | |||
+ | * soit les fichiers .tex sont modifiés | ||
+ | |||
+ | * soit le fichier de bibliographie a été modifié. A noter qu'on mettre les mêmes conditions d' | ||
+ | |||
+ | * Une exécution de LaTeX, une de BibTeX et deux autres exécutions de LaTeX (classique pour ceux qui connaissent un peu LaTeX) avant de retourner à la règle qui a appelé cette règle. | ||
+ | |||
+ | |||
+ | |||
+ | La règle pour la transformation en pdf reste la même. Idem pour le nettoyage. | ||
+ | |||
+ | |||
+ | |||
+ | ===== Makefile & Java ===== | ||
+ | |||
+ | ==== Un petit mot sur Java ==== | ||
+ | |||
+ | Java est un langage purement Orienté Objet (OO). Pour rappel, l'OO se caractérise par: | ||
+ | |||
+ | * La notion d' | ||
+ | |||
+ | * L' | ||
+ | |||
+ | * L' | ||
+ | |||
+ | * Le polymorphisme | ||
+ | |||
+ | |||
+ | |||
+ | Java n'est pas un langage compilé, mais interprété. Il est sensé être multiplateforme (au sens OS et hardware). Java fonctionne avec une machine virtuelle, la fameuse JVM... | ||
+ | |||
+ | |||
+ | |||
+ | Java se caractérise aussi par les nombreuses librairies disponibles. | ||
+ | |||
+ | |||
+ | |||
+ | On va essayer, dans ce chapitre, de mettre sur pied des Makefile un peu plus compliqués rolleyes.gif | ||
+ | |||
+ | |||
+ | |||
+ | Pour plus d' | ||
+ | |||
+ | |||
+ | |||
+ | ==== Un seul fichier source ==== | ||
+ | |||
+ | Tout projet de programmation peut se contenter d'un seul fichier source... A noter que, par convention, en Java, on écrit une seul classe par fichier. Ce n'est pas obligatoire, | ||
+ | |||
+ | |||
+ | |||
+ | Comme d' | ||
+ | |||
+ | |||
+ | < | ||
+ | JCC = javac | ||
+ | |||
+ | # | ||
+ | |||
+ | FILES = $(wildcard *.java | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * La première ligne définit le " | ||
+ | |||
+ | * La deuxième ligne (mise en commentaire) fait suite à la première si le jdk n'est pas défini dans le path. Il s'agit simplement d' | ||
+ | |||
+ | * Enfin, la dernière ligne permet de prendre en compte le fichier source. J'ai directement utilisé un ' | ||
+ | |||
+ | |||
+ | |||
+ | FILES = MaClasse.java | ||
+ | |||
+ | |||
+ | |||
+ | La règle est fort simple : | ||
+ | |||
+ | |||
+ | < | ||
+ | all: $(FILES) | ||
+ | |||
+ | | ||
+ | </ | ||
+ | |||
+ | |||
+ | Rien de bien compliqué à ce stade du tuto. | ||
+ | |||
+ | |||
+ | |||
+ | Comme d' | ||
+ | |||
+ | |||
+ | |||
+ | ==== Plusieurs fichiers sources et pas de package ==== | ||
+ | |||
+ | Ceux qui connaissent qq peu Java savent que la " | ||
+ | |||
+ | |||
+ | |||
+ | Sachant cela, on a deux possibilités : | ||
+ | |||
+ | * On reprend le Makefile de la section précédente en modifiant uniquement la variable FILES de la façon suivante: | ||
+ | |||
+ | |||
+ | |||
+ | FILES = $(wildcard *.java) | ||
+ | |||
+ | |||
+ | |||
+ | c' | ||
+ | |||
+ | * On se base sur la compilation en domino. On obtient alors ceci: | ||
+ | |||
+ | |||
+ | < | ||
+ | FILES = $(wildcard *.java) | ||
+ | |||
+ | TARGET | ||
+ | </ | ||
+ | |||
+ | |||
+ | Le fichier ' | ||
+ | |||
+ | |||
+ | < | ||
+ | all: $(FILES) | ||
+ | |||
+ | | ||
+ | </ | ||
+ | |||
+ | |||
+ | Dans ce cas, on laisse Java gérer la compilation. Mais il est fort probable que Java va recompiler tous les fichiers. | ||
+ | |||
+ | |||
+ | |||
+ | ==== Plusieurs fichiers sources et package ==== | ||
+ | |||
+ | === La notion de package === | ||
+ | |||
+ | On va, maintenant, mettre au point des (il y en aura plusieurs) Makefile plus compliqués. | ||
+ | |||
+ | |||
+ | |||
+ | Java permet de regrouper des classes ayant un lien entre elles sous le même chapeau. Ce chapeau se nomme package. Un package peut être de 'haut niveau' | ||
+ | |||
+ | |||
+ | |||
+ | package nom_package(.nom_sous_package)*; | ||
+ | |||
+ | |||
+ | |||
+ | Pour ceux qui ne connaissent pas ce genre de notation (on appelle cela une grammaire BNF), cela signifie que le mot clé package est suivi du nom du package. Si il y a des sous packages, ils seront séparés par un point. ' | ||
+ | |||
+ | |||
+ | |||
+ | La notion de package est aussi liée à une notion d' | ||
+ | |||
+ | |||
+ | |||
+ | Dès lors, la plupart des projets de développement en Java ayant une certaine importance comporteront plusieurs packages. | ||
+ | |||
+ | |||
+ | |||
+ | Supposons qu'on travaille sur un projet ayant les packages suivants : | ||
+ | |||
+ | * fr.lip6.tools | ||
+ | |||
+ | * fr.lip6.stopset | ||
+ | |||
+ | * fr.lip6.stopset.bloomfilter | ||
+ | |||
+ | * fr.lip6.stopset.bloomfilter.hashfunction | ||
+ | |||
+ | * fr.lip6.stopset.couplelist | ||
+ | |||
+ | |||
+ | |||
+ | Si on considère qu'on travaille dans le répertoire : / | ||
+ | |||
+ | |||
+ | |||
+ | On aura donc les répertoires suivants : | ||
+ | |||
+ | |||
+ | < | ||
+ | / | ||
+ | |||
+ | / | ||
+ | |||
+ | / | ||
+ | |||
+ | / | ||
+ | |||
+ | / | ||
+ | </ | ||
+ | |||
+ | |||
+ | Note : je passe sur la notion de classpath qui est inhérente aux packages. | ||
+ | |||
+ | |||
+ | |||
+ | === L' | ||
+ | |||
+ | On va utiliser trois types de Makefile: | ||
+ | |||
+ | |||
+ | |||
+ | * Le Makefile racine, qui aura pour mission d' | ||
+ | |||
+ | * Le Makefile de chaque sous-répertoire. Il devra appeler un Makefile permettant d' | ||
+ | |||
+ | * Le Makefile.include, | ||
+ | |||
+ | |||
+ | |||
+ | === Le Makefile propre à chaque package === | ||
+ | |||
+ | C'est un Makefile assez con... | ||
+ | |||
+ | |||
+ | |||
+ | Il doit se trouver dans chaque sous-répertoire correspondant à un package. Le sous-répertoire doit, of course, contenir du code. Il devra donc être placé dans les 5 sous-répertoires définit supra. | ||
+ | |||
+ | |||
+ | |||
+ | Ces Makefile auront la forme suivante (suppons qu'il s' | ||
+ | |||
+ | < | ||
+ | |||
+ | TOP_DIR | ||
+ | |||
+ | PACKAGE_DIR | ||
+ | |||
+ | PACKAGE_NAME | ||
+ | |||
+ | | ||
+ | |||
+ | include $(TOP_DIR)/ | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * On définit trois variables (TOP_DIR, PACKAGE_DIR, | ||
+ | |||
+ | * TOP_DIR est la variable la plus importante. Elle indique le répertoire de base, celui à partir duquel la JVM pourra chercher après les différents packages que nous avons créé. | ||
+ | |||
+ | * PACKAGE_DIR indique le répertoire du package dans lequel nous sommes. Ca sera utilisé pour la compilation. Rappel: en Java, dès qu'il y a des packages, la compilation doit se faire à partir de la racine des packages. Un ordre de compilation devrait donc ressembler à : | ||
+ | |||
+ | |||
+ | < | ||
+ | cd / | ||
+ | |||
+ | javac fr/ | ||
+ | </ | ||
+ | |||
+ | |||
+ | * PACKAGE_NAME ne sera pas utilisé dans ce tuto. On définit simplement le nom du package. C'est utile pour la génération de documentation via l' | ||
+ | |||
+ | * L' | ||
+ | |||
+ | |||
+ | |||
+ | === Le Makefile racine === | ||
+ | |||
+ | Le Makefile racine, c'est celui qui va appeler lq on lancera la commande : | ||
+ | |||
+ | |||
+ | |||
+ | make | ||
+ | |||
+ | |||
+ | |||
+ | Il a pour mission d' | ||
+ | |||
+ | |||
+ | |||
+ | Il contient 2 variables: | ||
+ | |||
+ | |||
+ | < | ||
+ | #root makefile. | ||
+ | |||
+ | | ||
+ | |||
+ | PACKAGE = . | ||
+ | |||
+ | | ||
+ | |||
+ | SUBDIRS = \ | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * PACKAGE définit le répertoire racine. Comme le fichier Makefile se trouve dans / | ||
+ | |||
+ | * SUBDIRS Il faut voir cette variable comme une sorte tableau, où chaque cellule contient le répertoire correspondant à un package contenant des sources. Comme vous l'avez compris, c'est sur base de ce tableau qu'on va aller appeler les Makefiles se trouvant dans les répertoires/ | ||
+ | |||
+ | |||
+ | |||
+ | Comme prévu, on se contente de deux règles : | ||
+ | < | ||
+ | |||
+ | |||
+ | all: | ||
+ | |||
+ | @@for p in $(SUBDIRS); do \ | ||
+ | |||
+ | echo ' | ||
+ | |||
+ | make -C $(PACKAGE)/ | ||
+ | |||
+ | done | ||
+ | |||
+ | | ||
+ | |||
+ | clean: | ||
+ | |||
+ | @@for p in $(SUBDIRS); do \ | ||
+ | |||
+ | echo ' | ||
+ | |||
+ | make -C $(PACKAGE)/ | ||
+ | |||
+ | done | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * On va boucler sur le contenu du ' | ||
+ | |||
+ | |||
+ | |||
+ | $$p | ||
+ | |||
+ | * Que signifie l' | ||
+ | |||
+ | |||
+ | |||
+ | make -C $(PACKAGE)/ | ||
+ | |||
+ | |||
+ | |||
+ | ??? Décomposons là... | ||
+ | |||
+ | * l' | ||
+ | |||
+ | * l' | ||
+ | |||
+ | |||
+ | |||
+ | Entre dans le répertoire directory | ||
+ | |||
+ | Quitte le répertoire directory | ||
+ | |||
+ | |||
+ | |||
+ | * Le all indique qu'on va exécuter la règle par défaut du Makefile appelé. Note: le Makefile appelé ne contient que la déclaration de variables. Cpdt, rappelez-vous qu'il fait appel à un Makefile.include. C'est donc dans ce Makefile.include que se trouvera la règle ' | ||
+ | |||
+ | * Note: en principe, dans un Makefile, une instruction ou une déclaration de variable doit tenir sur une seule ligne. L' | ||
+ | |||
+ | |||
+ | |||
+ | / | ||
+ | |||
+ | |||
+ | |||
+ | Afin d' | ||
+ | |||
+ | * Je n' | ||
+ | |||
+ | |||
+ | |||
+ | === Le Makefile.include === | ||
+ | |||
+ | C'est ici que ça devient compliqué. Je vais essayer d' | ||
+ | |||
+ | |||
+ | |||
+ | Ce fichier doit se trouver à la racine de nos packages (cfr la condition de compilation de classe packagisée en Java). | ||
+ | |||
+ | |||
+ | |||
+ | Ce fichier s' | ||
+ | |||
+ | |||
+ | |||
+ | On va définir dans Makefile.include deux types de variables; | ||
+ | |||
+ | |||
+ | |||
+ | * des variables d' | ||
+ | |||
+ | < | ||
+ | |||
+ | # set here the target dir for all classes | ||
+ | |||
+ | CLASS_DIR | ||
+ | |||
+ | # | ||
+ | |||
+ | LOCAL_CLASS_DIR = $(CLASS_DIR)/ | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Explication :** | ||
+ | |||
+ | * CLASS_DIR définit la base des packages. | ||
+ | |||
+ | * JAVA_CLASS (ici commenté et sans valeur) définit l' | ||
+ | |||
+ | * LOCAL_CLASS_DIR définit le répertoire ' | ||
+ | |||
+ | |||
+ | |||
+ | * des variables ' | ||
+ | |||
+ | < | ||
+ | |||
+ | JCC = javac | ||
+ | |||
+ | FILES = $(wildcard *.java) | ||
+ | |||
+ | </ | ||
+ | |||
+ | Rien de spécial à dire. | ||
+ | |||
+ | |||
+ | |||
+ | On passe à l' | ||
+ | |||
+ | < | ||
+ | |||
+ | #new rule for java | ||
+ | |||
+ | .SUFFIXES: | ||
+ | |||
+ | .SUFFIXES: .java .class | ||
+ | |||
+ | | ||
+ | |||
+ | #magical command that tells make to find class files in another dir | ||
+ | |||
+ | vpath %.class $(LOCAL_CLASS_DIR) | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * .SUFFIXES => permet de faire en sorte que les suffixes déclarés (dans notre cas, .java et .class) puissent être utilisés dans une règle de suffixe, çàd que le nom de la règle sera (dans notre cas): | ||
+ | |||
+ | |||
+ | |||
+ | .java.class | ||
+ | |||
+ | |||
+ | |||
+ | * vpath est une instruction magique qui permet de trouver les sources dépendantes de la source courante. Dans certains cas, on peut l' | ||
+ | |||
+ | |||
+ | |||
+ | **Les règles seront les suivantes :** | ||
+ | |||
+ | |||
+ | < | ||
+ | all: classes | ||
+ | |||
+ | | ||
+ | |||
+ | classes: $(FILES: | ||
+ | |||
+ | | ||
+ | |||
+ | .java.class: | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | |||
+ | clean: | ||
+ | |||
+ | @@ echo 'rm -f *~ *.class core *.bak *# $(LOCAL_CLASS_DIR)/ | ||
+ | |||
+ | @@rm -f *~ *.class core *.bak * | ||
+ | |||
+ | </ | ||
+ | |||
+ | **Petite explication :** | ||
+ | |||
+ | * Il y a des règles en cascade... ' | ||
+ | |||
+ | * La règle de suffixe. Rien de particulier (il faut connaitre un min Java pour comprendre). A noter que la variable ' | ||
+ | |||
+ | * La règle ' | ||
+ | |||
+ | |||
+ | |||
+ | ===== Conclusion & ressources ===== | ||
+ | |||
+ | Bon, voilà. C'est fini. | ||
+ | |||
+ | |||
+ | |||
+ | Je suis loin d' | ||
+ | |||
+ | |||
+ | |||
+ | Maintenant, le meilleur moyen d' | ||
+ | |||
+ | |||
+ | |||
+ | Comme ressource sur le Web, je donnerai un seul lien: http:// | ||
+ | |||
+ | |||
+ | |||
+ | --- // |
dev/makefile.txt · Dernière modification : 2018/11/17 12:53 de 127.0.0.1