Département d Informatique Faculté des Sciences de Luminy Programmation Unix Vendredi 17 décembre 2010 Durée 3h Licence MI, UE INF5, resp. E. Thiel Notes de Cours/TD/TP autorisées; autres documents, calculettes, ordinateurs interdits. Ne répondez pas à plusieurs questions en même temps; respectez le découpage de l énoncé, sous peine de nullité. Certaines questions sont plus faciles que d autres, lisez bien l énoncé avant de commencer. I. Script bash générant des modules C et leur Makele (20 points) On se propose d écrire le script bash genmoc.sh pour générer des modules C, générer un Makele pour les compiler, et ajouter des modules dans le Makele. On rappelle qu un module C toto est constitué de deux chiers toto.c et toto.h. On souhaite qu ils aient la forme suivante : toto.h toto.c /* toto.h - 17/12/2010 */ /* toto.c - 17/12/2010 */ #ifndef TOTO_H #dene TOTO_H /* ici : types et prototypes */ #include "toto.h" /* ici : corps des fonctions */ #endif /* TOTO_H */ 1) Le mécanisme #ifndef TOTO_H... présent dans le chier.h s appelle une garde, il est destiné à garantir l unicité de l inclusion de la partie types et prototypes. Pour un chier toto.h, le nom de la garde est TOTO_H. Écrire la fonction nom_garde, prenant en argument le nom du chier.h. La fonction afche sur la sortie standard le nom de la garde. 2) Écrire la fonction gen_point_h, prenant en argument le nom du chier.h à générer. La fonction crée ou écrase le chier, sous la forme dénie dans l encadré ci-dessus, avec le nom de la garde correspondant au nom du chier, et avec la date de création du chier. Pour obtenir la date courante sous cette forme, on utilisera la commande : date +%d/%m/%y 3) Écrire la fonction gen_point_c, prenant en argument le nom du chier.c à générer. La fonction crée ou écrase le chier, sous la forme dénie dans l encadré ci-dessus, avec la date de création du chier, et l inclusion du chier.h correspondant. 4) On souhaite générer un Makele pour compiler un programme constitué de plusieurs modules. En supposant que l exécutable à produire s appelle par exemple monprog, le Makele commencera par :.c.o : gcc -Wall -c $*.c OBJS = EXEC = monprog Comme on le voit, la variable OBJS est déclarée vide. Par la suite, elle contiendra la liste des chiers.o nécessaires pour créer l exécutable (on s en occupera dans une autre question). Programmation Unix, UE SIN3U7 et SIN4U2, Edouard Thiel CC BY-NC http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ens/unix/ 1
4.a) Écrire la suite du Makele, en rajoutant une règle pour créer l exécutable, ainsi qu une règle de nettoyage (utiliser les variables EXEC et OBJS). 4.b) Écrire la fonction gen_makele, prenant en argument le nom de l exécutable. La fonction renomme l ancien Makele (s il existe) en Makele.bak, puis crée le nouveau Makele tel que décrit ci-dessus. Attention, pensez à protéger les dollars... 5) On suppose que l affectation OBJS = module1.o module2.o... est sur une seule ligne dans le Makele, et qu il y a au moins un espace de part et d autre du =. Écrire la fonction cherche_affectation, prenant en argument un nom de variable. La fonction cherche la ligne de l affectation de la variable dans le Makele en faisant des read (ne pas utiliser les commandes de la famille grep). Si elle trouve la ligne, elle afche sur la sortie standard une seule ligne sous la forme suivante : numéro de la ligne, espace, partie droite de l affectation; puis elle réussit. Sinon, la fonction afche un message d erreur et échoue. 6) Écrire la fonction ajout_obj, prenant en argument le nom d un chier.o. La fonction teste si le Makele existe; si ce n est pas le cas, elle afche un message d erreur et se termine. Elle cherche ensuite la ligne d affectation de la variable OBJS à l aide de la fonction cherche_affectation, sinon elle afche un message d erreur et se termine. Enn, elle remplace la ligne en question en insérant tout de suite à droite du = le nom du chier.o reçu en argument. 7) On suppose disposer de la fonction af_usage (ne pas l écrire!) qui afche sur la sortie standard le texte suivant : USAGE: genmoc.sh -h --help : afche cette aide genmoc.sh [-m appli] [-c] [-o] modules -m appli : crée un Makele pour le programme appli -c : crée les.c et.h des modules -o : met à jour la variable OBJS pour les modules Écrire le programme principal du script correspondant à l usage demandé. Le programme commence par analyser les arguments du script, puis génère (éventuellement) un Makele, puis pour chaque nom de module en argument, crée (si demandé) le module C, et insère (si demandé) le nom du chier.o dans la liste OBJS du Makele. Chaque fois qu une action est faite, le script afche un message informant de l action en cours. Programmation Unix, UE SIN3U7 et SIN4U2, Edouard Thiel CC BY-NC http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ens/unix/ 2
Correction I. Script bash générant des modules C et leur Makele (20 points) 1) Le mécanisme #ifndef TOTO_H... présent dans le chier.h s appelle une garde, il est destiné à garantir l unicité de l inclusion de la partie types et prototypes. Pour un chier toto.h, le nom de la garde est TOTO_H. Écrire la fonction nom_garde, prenant en argument le nom du chier.h. La fonction afche sur la sortie standard le nom de la garde. nom_garde () { echo "$1" tr [a-z]. [A-Z] 2) Écrire la fonction gen_point_h, prenant en argument le nom du chier.h à générer. La fonction crée ou écrase le chier, sous la forme dénie dans l encadré ci-dessus, avec le nom de la garde correspondant au nom du chier, et avec la date de création du chier. Pour obtenir la date courante sous cette forme, on utilisera la commande : date +%d/%m/%y gen_point_h () { local ngarde=$(nom_garde "$1") cat > "$1" << /* $1 - $(date +%d/%m/%y ) */ #ifndef $ngarde #dene $ngarde #endif /* $ngarde */ 3) Écrire la fonction gen_point_c, prenant en argument le nom du chier.c à générer. La fonction crée ou écrase le chier, sous la forme dénie dans l encadré ci-dessus, avec la date de création du chier, et l inclusion du chier.h correspondant. gen_point_c () { local nomh="${1%.c.h" cat > "$1" << /* $1 - $(date +%d/%m/%y ) */ #include "$nomh" 4) On souhaite générer un Makele pour compiler un programme constitué de plusieurs modules. En supposant que l exécutable à produire s appelle par exemple monprog, le Makele commencera par : Programmation Unix, UE SIN3U7 et SIN4U2, Edouard Thiel CC BY-NC http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ens/unix/ 3
.c.o : gcc -Wall -c $*.c OBJS = EXEC = monprog Comme on le voit, la variable OBJS est déclarée vide. Par la suite, elle contiendra la liste des chiers.o nécessaires pour créer l exécutable (on s en occupera dans une autre question). 4.a) Écrire la suite du Makele, en rajoutant une règle pour créer l exécutable, ainsi qu une règle de nettoyage (utiliser les variables EXEC et OBJS). 4.b) Écrire la fonction gen_makele, prenant en argument le nom de l exécutable. La fonction renomme l ancien Makele (s il existe) en Makele.bak, puis crée le nouveau Makele tel que décrit ci-dessus. Attention, pensez à protéger les dollars... gen_makele () { test -f Makele && mv -f Makele Makele.bak cat > Makele <<.c.o : gcc -Wall -c \$*.c OBJS = EXEC = $1 \$(EXEC) : \$(OBJS) gcc \$(OBJS) -o \$(EXEC) clean :: rm -f \$(OBJS) \$(EXEC) 5) On suppose que l affectation OBJS = module1.o module2.o... est sur une seule ligne dans le Makele, et qu il y a au moins un espace de part et d autre du =. Écrire la fonction cherche_affectation, prenant en argument un nom de variable. La fonction cherche la ligne de l affectation de la variable dans le Makele en faisant des read (ne pas utiliser les commandes de la famille grep). Si elle trouve la ligne, elle afche sur la sortie standard une seule ligne sous la forme suivante : numéro de la ligne, espace, partie droite de l affectation; puis elle réussit. Sinon, la fonction afche un message d erreur et échoue. cherche_affectation () { local n=0 trouve=false var egal valeur while read var egal valeur do let n=n+1 # ou ((n++)) if [ "$var" = "$1" -a "$egal" = "=" ]; then trouve=true ; break done < Makele if! $trouve ; then echo "cherche_affectation: ne trouve pas $1 = " 1>&2 return 1 echo "$n $valeur" return 0 Programmation Unix, UE SIN3U7 et SIN4U2, Edouard Thiel CC BY-NC http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ens/unix/ 4
6) Écrire la fonction ajout_obj, prenant en argument le nom d un chier.o. La fonction teste si le Makele existe; si ce n est pas le cas, elle afche un message d erreur et se termine. Elle cherche ensuite la ligne d affectation de la variable OBJS à l aide de la fonction cherche_affectation, sinon elle afche un message d erreur et se termine. Enn, elle remplace la ligne en question en insérant tout de suite à droite du = le nom du chier.o reçu en argument. ajout_obj () { local res n listeo tmp if [! -f Makele ]; then echo "ajout_obj: Makele inexistant" 1>&2 return res=$(cherche_affectation OBJS) if [ $?!= 0 ]; then echo "ajout_obj: ne trouve pas OBJS =" 1>&2 return # On récupère le premier mot et la suite dans res read n listeo <<< "$res" # On remplace la ligne numéro n dans le Makele tmp=makele.$$ true > "$tmp" head -$((n-1)) Makele >> "$tmp" echo "OBJS = $1 $listeo" >> "$tmp" tail -n +$((n+1)) Makele >> "$tmp" mv -f Makele Makele.bak # pas demandé mv -f "$tmp" Makele 7) On suppose disposer de la fonction af_usage (ne pas l écrire!) qui afche sur la sortie standard le texte suivant : USAGE: genmoc.sh -h --help : afche cette aide genmoc.sh [-m appli] [-c] [-o] modules -m appli : crée un Makele pour le programme appli -c : crée les.c et.h des modules -o : met à jour la variable OBJS pour les modules Cette fonction s écrit ainsi : af_usage () { cat << USAGE: $0 -h --help : afche cette aide $0 [-m appli] [-c] [-o] modules : -m appli : crée un Makele pour le programme appli -c : crée les.c et.h des modules -o : met à jour la variable OBJS pour les modules Écrire le programme principal du script correspondant à l usage demandé. Le programme commence par analyser les arguments du script, puis génère (éventuellement) un Makele, puis pour chaque nom de module en argument, crée (si demandé) le module C, et insère (si demandé) le nom du chier.o dans la liste OBJS du Makele. Chaque fois qu une action est faite, le script afche un message informant de l action en cours. Programmation Unix, UE SIN3U7 et SIN4U2, Edouard Thiel CC BY-NC http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ens/unix/ 5
#! /bin/bash # Emplacement des fonctions # Prog principal if [ $# = 0 ]; then af_usage 1>&2 ; exit 1 flag_m=false appli= flag_o=false flag_c=false while [ $#!= 0 ] do case "$1" in -h --help) af_usage ; exit 0 ;; -m) flag_m=true ; appli="${2:-undened" ; shift 2 ;; -o) flag_o=true ; shift ;; -c) flag_c=true ; shift ;; *) break ;; esac done if $flag_m ; then echo "Création du Makele pour le programme $appli..." gen_makele "$appli" for module ; do if $flag_c ; then echo "Création du module $module..." gen_point_h "$module.h" gen_point_c "$module.c" if $flag_o ; then echo "Mise à jour de OBJS..." ajout_obj "$module.o" done exit 0 Programmation Unix, UE SIN3U7 et SIN4U2, Edouard Thiel CC BY-NC http://pageperso.lif.univ-mrs.fr/~edouard.thiel/ens/unix/ 6