Faites votre travail avec make!

ArticleCategory: []

Software Development

AuthorImage:[Here comes a small picture of you]

[Wilbert Berendsen]

TranslationInfo:[Info concerning writer(s) and translator(s)]

original in nl Wilbert Berendsen

nl to en Philip de Groot

en to fr Iznogood

AboutTheAuthor:[a short biography about the author]

Wilbert Berendsen est un professeur de musique et un utilisateur de Linux enthousiaste. Autrefois, il programmait beaucoup en assembleur pour le Z80. Aujourd''hui, il utilise Linux pour tout son travail. Simplement pour le plaisir, il écrit des articles d'introduction et maintient un petit site web sur http://www.xs4all.nl/~wbsoft/. Viva open source!

Abstract:[a small summary/description of this article]

Cet article montre le fonctionnement de make et comment il peut être utilisé pour d'autres choses que le développement logiciel.

ArticleIllustration:[illustration]

[Illustration]

ArticleBody:[The real article: put the text and html-codes here]

Introduction

A peu près toutes les personnes qui utilisent Linux ont un jour fait appel au programme make. Il fait ce qu'on attend de lui lors de la construction d'un programme ou du noyau à partir du code source, lors de l'installation d'un paquetage, ainsi de suite. 'Make' est un outil important pour le développement logiciel. Il possède, néanmoins, beaucoup d'autres capacités!

Dans ce document, nous verrons que make peut être très puissant pour le travail de tous les jours, tel qu'écrire des articles, des livres ou créer un joli site web. Pendant cette introduction, plusieurs autres 'trucs' d'Unix seront abordés. A la fin de l'histoire, quelques trucs supplémentaires seront présentés sur l'utilisation de make. Remarque : nous parlons de Linux mais en principe, il est possible d'utiliser make sur tout système d'exploitation.

Exemple: construire un site web

Nous voulons créer un site web qui sera maintenu par plusieurs personnes. Jans s'occupe de deux pages et en assure la maintenance. Piet gère la mise en page.

Nous avons besoin d'un système simple pour séparer l'apparence du contenu. Une solution puissante est : lire le contenu à partir d'une base de données, à chaque fois que la page est demandée. Par exemple, PHP et Microsoft Active Server Pages fonctionnent de cette manière. Nous n'avons néanmoins que la possibilité de stocker du HTML (HyperText Markup Language). De plus, le contenu ne doit pas changer trop souvent pour maintenir la base de données de manière efficace.

Un site web sera construit en utilisant des commandes simples, .

Par exemple, Piet met l'en-tête du site dans header.html et le contenu du pied de page dans footer.html. header.html peut ressembler à ceci :

<html><!-- l'en-tête -->
<head>
<title>Productions Piet et Jan</title>
</head>
<body bgcolor="white">
<table border="0" width="100%"><tr>
<td bgcolor="#c040ff" valign="top">
Ceci est notre site<br>
Quelques bricoles écrites ici.<br>
Nous sommes très interactifs<br>
donc c'est notre numéro de téléphone :<br>
<b>0123-456789</b>
</td><td valign="top">
<!-- Mettre le contenu ici -->
Et ici footer.html:
<!-- le pied de page -->
</td></tr></table>
</body></html>
Par exemple, les commandes Unix pour fabriquer la page finale à partir de l'index.html de Jans sont :
cat header.html  /home/jan/Docs/website/index.html
echo -n '<hr>Dernière modification: '
date '+%A %e %B'
cat footer.html
Référez-vous aux pages de manuel de ces commandes. Le fichier final, résultat des commandes ci-dessus, est "pipé" vers la sortie standard, qui est stockée dans un fichier :
{
  cat header.html  /home/jan/Docs/website/index.html
  echo -n '<hr>dernière odification: '
  date '+%A %e %B'
  cat footer.html
} > /home/piet/public_html/index.html
Cette procédure peut être répétée avec l'autre fichier, offer.html. En fait nous créons un petit script qui permet la construction de notre site web.

Néanmoins, exécuter manuellement cette commande n'est pas réalisable. Nous pouvons créer un script shell qui est exécuté à chaque fois que Jans a mis à jour son index. Néanmoins, si Piet décide de changer l'en-tête ou le pied de page, ce script doit aussi être exécuté! D'un autre coté, si Jans n'a rien changé pendant une journée, le script ne doit pas être exécuté. Nous utilisons Linux, nous voulons donc utiliser une solution élégante (comprendre : automatique)!

C'est là que make intervient.

Première rencontre avec make

Le manuel-info de GNU make est un document fantastique. Néanmoins, dès le départ, il met l'accent sur un environnement de programmation. Pour cette raison, j'essaie de présenter les fonctions de make au sens général :
    make détermine comment un jeu de commandes doit être exécuté, en fonction
 de la date du fichier cible et de la date des fichiers source. 
En d'autres termes : si un des fichiers source, nécessaire à la création d'un fichier cible, est plus récent que ce fichier cible, un jeu de commandes est exécuté. Le but de ces commandes est de mettre à jour le fichier cible.

Le fichier cible est la 'destination' et les fichiers source sont les `conditions préalables'. Les commandes sont exécutées si l'une des `conditions préalables' est plus récente que le fichier cible (ou si la cible n'existe pas). Si toutes les conditions préalables sont plus anciennes ou possédent la même date que la cible, alors les commandes ne sont pas exécutées et la cible est considérée comme étant à jour.

Dans le répertoire de travail courant, un fichier doit être créé avec le nom Makefile. Ce fichier contient les informations nécessaires à make pour faire son travail correctement. Une fois que nous avons le Makefile, la seule chose à faire est de taper 'make', et les commandes, nécessaires pour créer un nouveau fichier cible, sont exécutées automatiquement.

Make est appelé avec la commande

make cible1 cible2 ....

cible est optionnel (si cible est omis, la première cible du Makefile est utilisée). Make cherche toujours un Makefile dans le répertoire courant. Il est possible de fournir plus d'une cible.

Syntaxe du Makefile

Le Makefile peut être crée avec un éditeur et ressemble à :
# Ceci est un exemple de Makefile.
# Les commentaires peuvent être mis après un dièse (#).

cible: conditions préalables
	commande
        
cible: conditions préalables
	commande 
        
# etc, etc.
Nous démarrons avec une cible, suivie par deux points (:) et les conditions préalables nécessaires. Dans le cas de plusieurs conditions préalables, il est possible de terminer la ligne avec un antislash (\) et de continuer sur la ligne suivante.

Sur la(es) ligne(s) suivante(s), une ou plusieurs commandes sont présentées. Chaque ligne est considérée comme une commande unique. Si vous voulez utiliser de multiples lignes pour une commande, vous devez mettre des antislashes (\) à la fin des lignes. Make reliera les commandes comme si elles avaient été saisies sur une seule ligne. Dans cette situation, nous devons séparer les commandes par un point virgule (;) de manière à éviter les erreurs dans l'exécution du shell.

Note: Les commandes doivent être indentées avec une TAB, pas avec 8 espaces!

Make lit le Makefile et détermine pour chaque cible (en commençant par la première) si la commande doit être exécutée. Chaque cible, associée aux conditions préalables et aux règles, est présentée comme une 'règle' (rule).

Si make est exécuté sans argument, seule la première cible sera exécutée.

Un Makefile pour notre exemple

Pour notre exemple, le Makefile doit ressembler à ceci :
# Ce Makefile construit le site web de Piets et Jans, les mangeurs de pommes 
de terre.

all: /home/piet/public_html/index.html /home/piet/public_html/offer.html

/home/piet/public_html/index.html:  header.html footer.html \
                                    /home/jan/Docs/website/index.html
	{ \
          cat header.html  /home/jan/Docs/website/index.html ;\
          echo -n '<hr>Dernière modification: '               ;\
          date '+%A %e %B'                                   ;\
          cat footer.html                                    ;\
        } > /home/piet/public_html/index.html

/home/piet/public_html/offer.html:  header.html footer.html \
                                    /home/jan/Docs/website/offer.html
	{ \
          cat header.html  /home/jan/Docs/website/index.html ;\
          echo -n '<hr>Dernière modification: '               ;\
          date '+%A %e %B'                                   ;\
          cat footer.html                                    ;\
        } > /home/piet/public_html/offer.html

# fin

Maintenant, nous avons trois cibles, 'all' et les fichiers index.html et offer.html du site. La seule fonction de la cible 'all' est d'avoir les deux autres comme conditions préalables. Ils sont testés tous les deux. 'all' en lui-même n'étant pas un nom de fichier, la cible 'all' sera toujours exécutée. (Plus tard, nous présenterons une manière plus élégante de définir des cibles qui ne sont pas des fichiers).

Si l'en-tête et le pied de page sont modifiés, les deux pages seront mises à jour. Si Jans modifie une de ses pages, seule la page modifiée sera mise à jour. Exécuter la commande 'make' fait le travail!

Bien sûr, le Makefile a un inconvénient : il n'est pas facile à vérifier. Heureusement, plusieurs techniques existent pour rendre les choses plus simples!

Améliorer le Makefile

Variables

Grâce aux variables, un Makefile peut être beaucoup simplifié. Les variables sont définies comme suit :
variable = value
Nous nous référons à une variable avec l'expression $(variable). Si nous incorporons ceci dans un Makefile, il s'améliore :
# Ce Makefile construit le site web de Piets et Jans, les mangeurs de pomme de 
terre.

# Répertoire dans lequel le site web est stocké :
TARGETDIR = /home/piet/public_html

# Répertoire de Jans :
JANSDIR = /home/jan/Docs/website

# Les fichiers nécessaires à la mise en page :
LAYOUT = header.html footer.html

all: $(TARGETDIR)/index.html $(TARGETDIR)/offer.html

$(TARGETDIR)/index.html:  $(LAYOUT) $(JANSDIR)/index.html
	{ \
          cat header.html $(JANSDIR)/index.html     ;\
          echo -n '<hr>Dernière modification: '      ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $(TARGETDIR)/index.html

$(TARGETDIR)/offer.html:  $(LAYOUT) $(JANSDIR)/offer.html
	{ \
          cat header.html  $(JANSDIR)/index.html    ;\
          echo -n '<hr>Dernière modification: '      ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $(TARGETDIR)/offer.html

# fin
C'est une bonne habitude d'utiliser des majuscules pour les variables. Maintenant, il est plus facile de changer le répertoire cible, par exemple.

Si vous voulez, il est possible de définir une autre méthode pour chaque document que vous souhaitez ajouter dans la mise en page. Que devons-nous faire si plusieurs documents doivent être ajoutés dans la même mise en page ? Le Makefile deviendrait très gros, alors que plusieurs redondances existent. Cela peut aussi être simplifié!

Règles de modèle

Les `Règles de modèle' permettent d'utiliser le même jeu de commandes sur tous les types de cibles différents.

Si les Règles de modèle sont utilisées, la syntaxe de la ligne change; un champ de modèle est ajouté :

Multiples cibles : modèle : condition préalable condition préalable ...
	commande
Le modèle est une expression qui doit être applicable à toutes les cibles. Un caractère pourcentage (%) est utilisé pour incorporer les parties variables d'un nom de cible.

Un exemple:

/home/bla/target1.html /home/bla/target2.html: /home/bla/% : %
	commandes
Si make lit ceci, la ligne est étendue sur 2 lignes. Ici, le modèle détermine quelle partie du nom de la cible est incorporée dans le signe pourcentage.

Le caractère pourcentage dans le champ des conditions préalables représente la partie qui est copiée par ce signe pourcentage.

Make décompose ce qui précède de la manière suivante :

/home/bla/target1.html:	target1.html
	commandes
        
/home/bla/target2.html: target2.html
	commandes
Le caractère pourcentage dans le modèle `/home/bla/%' obtient par la cible `/home/bla/target1.html' la valeur `target1.html', étendant ainsi la condition préalable `%' à `target1.html'.

Pour notre site web, les règles suivantes seront incorporées :

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : $(JANSDIR)/% \
                                                  $(LAYOUT)
Maintenant, il nous reste un problème : comment utiliser les variables dans les commandes ? Les commandes étaient-elles légèrement différentes pour les deux cibles ?

Variables automatiques

Heureusement, make définit quelques variables pour lui-même. Quelques unes de ces variables sont appelées automatiques. Elles contiennent, pendant l'exécution de la commande (ou mieux : juste avant d'exécuter ces commandes), la valeur de la cible et/ou de la condition préalable.

La variable spéciale $\ est utilisée pour indiquer la première condition préalable et la variable $@ s'étend toujours à la cible courante.

En utilisant ces variables, il est possible de généraliser la règle complète comme suit :

$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : $(JANSDIR)/% \
                                                  $(LAYOUT)
	{ \
          cat header.html  $<                       ;\
          echo -n '<hr>Dernière modification: '      ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@
Voilà! Cette simple ligne fonctionne maintenant pour les deux fichiers !

Pour être complet, le Makefile entier est présenté avec quelques optimisations :

#  Ce Makefile construit le site web de Piets et Jans, les mangeurs de pomme 
de terre.

# répertoire où le site web est publié :
TARGETDIR = /home/piet/public_html

# répertoire de Jans :
JANSDIR = /home/jan/Docs/website

# Fichiers nécessaires à la mise en page :
LAYOUT = header.html footer.html

# Voici les pages web :
DOCS = $(TARGETDIR)/index.html $(TARGETDIR)/offer.html


# Ne changez rien en dessous de cette ligne ;-)
# -------------------------------------------------------------

all: $(DOCS)

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT)
	{ \
          cat header.html  $<                       ;\
          echo -n '<hr>Dernière modification: '         ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@

# fin
Ceci commence à ressembler à ce que cela devrait être. Si d'autres documents sont ajoutés, il est facile de les incorporer dans le Makefile, en utilisant la variable DOCS, sans trop avoir de saisies à faire.

A la fin, la personne qui maintient le Makefile devrait facilement comprendre le fonctionnement, sans de poser trop de questions !

Les dernières petites optimisations

Nous préfèrerions mentionner les documents dans DOCS, sans inclure le répertoire entier. Cela peut être fait avec ce qui suit (nous remplaçons DOCS au début du makefile par TEXTS):
TEXTS = index.html  offer.html  yetanotherfile.html

# Ne changez rien en dessous de cette ligne ;-)
# -------------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))

all: $(DOCS)

# etc
Ce que nous voyons ici est une fonction spéciale de make : au lieu d'un nom de variable, il est possible d'utiliser une expression complète entre parenthèses. De cette manière, il est possible de modifier des textes de plusieurs façons.

La commande spéciale $(addprefix prefix,list) ajoute un préfixe à chaque élément de la liste. Dans l'exemple, c'est le contenu de la variable TARGETDIR plus un slash (/).

Les objets listés sont séparés par des espaces. Pour cette raison, ce n'est pas une bonne idée de traiter des noms de fichiers avec espaces avec la commande make.

Pour conclure : au début, nous avons mentionné que la cible 'all' ne créerait pas un fichier avec le nom 'all' (cette ligne ne contient pas de commande) et comme résultat, cette cible est toujours exécutée. Mais comment gérer ce cas, si <accidentellement> un fichier existe avec ce nom, et qu'il est plus récent que tous les autres fichiers...?

Il y a une manière facile d'indiquer à make qu'une cible particulière doit être toujours exécutée et que cette cible ne correspond à aucun fichier sur le disque dur. Pour ce faire, la cible est marquée comme 'phony' (non réel). Ceci est fait comme suit :

.PHONY: all
Maintenant, le Makefile complet ressemble à ceci :
# Ce Makefile construit le site web de Piets et Jans, les mangeurs de pommes 
de terre.

# Répertoire où le site web est publié :
TARGETDIR = /home/piet/public_html

# Répertoire de Jans :
JANSDIR = /home/jan/Docs/website

# Fichier nécessaire pour la structure :
LAYOUT = header.html footer.html

# Voici les noms des pages web :
TEXTS = index.html  offer.html  autrefichier.html

# Ne changez rien en dessous de cette ligne ;-)
# ------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))
.PHONY: all

all: $(DOCS)

$(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT)
	{ \
          cat header.html  $<                       ;\
          echo -n '<hr>Dernière modification: '         ;\
          date '+%A %e %B'                          ;\
          cat footer.html                           ;\
        } > $@

# fin
Enregistrez ce fichier et oubliez-le ! A partir de maintenant, il est possible de maintenir vos pages web, peut être en utilisant votre crontab et pour séparer proprement la mise en page du contenu!

Remarque finale

Bien sûr, il est possible de modifier cet exemple pour d'autres situations.

Par exemple, la manière simpliste par laquelle le document est généré n'est pas sans risque d'erreurs : si Jans termine ses articles accidentellement par </body></html>, la plupart des navigateurs n'afficheront pas le pied de page créé par Piet. Si nous utilisons grep, perl ou tcl, il est possible d'ajouter quelques titres aux documents de Jans d'une manière astucieuse dans l'en-tête du site.

Bien sûr, Jans peut simplement écrire un texte et utiliser la commande sed pour changer toutes les lignes vides (retour chariot) en <P>:

sed -e 's/^\s*$/<p>/g'
Jans peut aussi écrire ses textes dans LyX et utiliser un programme comme lyx2html, pour le convertir en HTML. Il existe d'extraordinaires possibilités !

Une autre modèle de construction est aussi envisageable.

Nous n'avons pas pris en considération la façon de transférer (redimensionner, convertir ou compresser) les images dans le répertoire du site. Il est aussi possible d'automatiser ce processus!

Dans cet exemple, Piet doit avoir les droits de lecture dans le répertoire du site web de Jans. L'intérêt dans la séparation de ces taches est de pouvoir les mettre en pratique dans de grosses organisations. Piet peut même se loger à l'autre bout du monde ou monter son répertoire personnel par NFS. Les exemples peuvent aussi être utilisés pour le travail effectué par un utilisateur.

Espérons que les principes de fonctionnement du Makefile sont devenus plus clairs et que votre travail quotidien sera facilité par l'écriture d'un bon Makefile !

Trucs

Plus d'informations

Plus d'informations sur le fonctionnement de make et sur toutes les autres possibilités peuvent être trouvées dans le `Manuel GNU Make '. Vous pouvez lire ce manuel sur votre système Linux avec la commande suivante :
info make
Bien sûr, il est possible de lire le Manuel GNU Make avec les navigateurs d'aide GNOME et KDE ou le programme tkinfo.

Liens vers d'autres informations à propos de make:

Amusez-vous bien !