Pour une première approche constructive de la console sous Unix.
Plus d’excuses, il faut s’y mettre! 
Pourquoi “Hello, world !”?
“Hello, world !” est la première chose qu’un programmeur digne de ce nom écrit. Les programmes de type “Hello, world !” sont souvent l’objet de moqueries injustifiées. De par sa simplicité, ce petit programme permet pourtant beaucoup de choses:
o Se familiariser avec les bases d’un nouveau langage de programmation
o Tester le bon fonctionnement des outils de développement
o S’habituer à un nouvel environnement
o Connaître les commandes de bases d’un système d’exploitation
o Rendre hommage aux pionniers de l’informatique
o etc…
Comment écrit-on “Hello, world !” sur Unix?
Beaucoup de gens ne seront pas d’accord, mais la meilleure manière d’écrire “Hello, world !” sur Unix est en langage C.
o Prenez un éditeur de texte et introduisez le texte suivant:
 |
 |
#include <stdio.h>
int main (int argc, char **argv)
{
printf (”Hello, world !\n”);
return 0;
} |
o Sauvez ce texte dans un fichier nommé hello.c .
o Pour compiler depuis un interpréteur de commande Unix (shell), lancer la commande suivante:
 |
 |
$ cc hello.c |
Le signe $ ne fait évidemment pas partie de la commande, mais il s’agit de l’invite de commande par défaut de l’interpréteur de commande.
o Le compilateur génère un exécutable qui s’appelle par défaut a.out, ce qui veut simplement dire “Assembler output”. Pour exécuter le fichier, il suffit d’introduire la commande suivante:
 |
 |
$ ./a.out |
o On voit immédiatement s’afficher le résultat suivant :
 |
 |
Hello, world !
$ |
En utilisant la compilation séparée ?
Dès qu’on veut réaliser des projets plus importants qu’afficher “Hello, world !”, il est nécessaire de diviser le programme en plusieurs parties et de les compiler séparément.
Dans l’exemple suivant, nous allons utiliser la compilation séparée pour réaliser le même programme. Ceci nécessitera l’utilisation de 3 fichiers source:
o main.c contient le programme principal
o hello.c contient une fonction qui renvoie un pointeur sur le texte “Hello, world !”
o hello.h contient la déclaration de la fonction contenue dans hello.c. Ceci permet d’utiliser cette fonction depuis l’extérieur.
Comment procéder ?
o Le fichier hello.h contiendra le texte suivant:
 |
 |
const char * getHelloStr (void); |
o Le fichier hello.c contiendra le texte suivant:
 |
 |
#include “hello.h”
static const char *helloStr = “Hello, world !”;
const char * getHelloStr (void)
{
return helloStr;
} |
o Pour compiler le fichier hello.c, lancer la commande suivante:
 |
 |
$ cc -c hello.c |
Le compilateur génère un fichier hello.o contenant le code de la fonction getHelloStr(), ainsi que le texte “Hello, world !”.
o Le fichier main.c contiendra le texte suivant:
 |
 |
#include <stdio.h>
#include “hello.h”
int main (int argc, char **argv)
{
printf (”%s\n”, getHelloStr());
return 0;
} |
o Pour compiler le fichier main.c, lancer la commande suivante:
 |
 |
$ cc -c main.c |
Le compilateur génère un fichier main.o contenant un appel à printf() ainsi qu’à getHelloStr() .
o Nous disposons maintenant des fichiers hello.o et main.o . Pour générer un exécutable, il suffit d’entrer la commande suivante:
 |
 |
$ cc main.o hello.o |
o Le compilateur génère un exécutable qui s’appelle par défaut a.out . La commande :
 |
 |
$ ./a.out |
permet de lancer le programme.
o On voit immédiatement s’afficher le résultat suivant :
 |
 |
Hello, world !
$ |
Automatisons la compilation…
Lorsqu’on développe des projets d’une certaine importance, il y a rapidement beaucoup de fichiers source et il est fastidieux de lancer les bonnes commandes de compilation chaque fois qu’un fichier est modifié. Ceci peut de plus devenir une importante source d’erreurs. On risque en effet d’oublier de recompiler des fichiers. On peut aussi tout recompiler à chaque fois, mais cela peut prendre plusieurs heures pour des projets importants.
Heureusement, les concepteurs d’Unix ont les pieds sur terre et une solution simple et élégante a été développée pour automatiser les compilations: l’utilitaire make .
Pour utiliser make, il faut créer un fichier qui s’appelle Makefile . Ce fichier contient une description des dépendances entre les différents fichiers et permet à make de savoir quelles commandes exécuter. make fait toujours le minimum de travail pour satisfaire les dépendances, ce qui veut dire que le minimum de recompilations sont effectuées pour avoir un fichier exécutable à jour.
Pour savoir quels fichiers recompiler, make utilise la date de derniére modification des fichiers. Ainsi une compilation est lancée si le source est plus récent que le fichier objet correspondant.
Une règle simple:
make utilise un ensemble de règles pour décider quelles commandes exécuter. Pour notre exemple:
 |
 |
hello.o : hello.c hello.h
=> cc -c hello.c -o hello.o |
Les 2 lignes précédentes signifient les choses suivantes:
o La flèche ( => ) indique la présence d’un tabulateur, ce qui est obligatoire avant chaque ligne de commande.
o Le fichier hello.o dépend des fichiers hello.c et hello.h .
o La date de création des fichiers permet de savoir si une dépendance est satisfaite.
o La commande cc -c hello.c -o hello.o permet de satisfaire la dépendance.
Autrement dit : Si le fichier hello.c ou hello.h est plus récent que hello.o, exécuter la commande cc -c hello.c -o hello.o . Après l’exécution de la commande, un nouveau fichier hello.o est généré et ce fichier sera plus récent que hello.c et que hello.h, ce qui fait que la dépendance sera satisfaite.
Makefile minimal:
 |
 |
hello : main.o hello.o
cc main.o hello.o -o hello
main.o : main.c hello.h
cc -c main.c -o main.o
hello.o : hello.c hello.h
cc -c hello.c -o hello.o |
Pour savoir comment définir les dépendances, on peut voir quels fichiers sont inclus (#include “…”). Si hello.h est modifié, il faudra recompiler hello.c et main.c . Par contre, il n’y a pas besoin de s’occuper de stdio.h , ce fichier ne devant naturellement jamais être modifié.
Makefile plus portable:
L’un des grands avantages de make par rapport aux environnements de développement intégrés est la portabilité. �? quoi bon faire attention pour écrire du code portable si on utilise des outils propriétaires qui stockent les informations d’un projet dans des fichiers binaires totalement dépendants d’un environnement de développement et d’une plateforme ?
 |
 |
CC = cc
CFLAGS = -c
LD = $(CC)
LDFLAGS =
hello : main.o hello.o
$(LD) $(LDFLAGS) main.o hello.o -o hello
main.o : main.c hello.h
$(CC) $(CFLAGS) main.c -o main.o
hello.o : hello.c hello.h
$(CC) $(CFLAGS) hello.c -o hello.o
clean:
rm *.o *~ *.bak |
En utilisant des variables, on peut faciliter le portage d’un Makefile. Les noms des variables peuvent être choisis arbitrairement, mais certaines habitudes, comme l’utilisation de majuscules, augmentent la lisibilité.
Supposons qu’on veuille utiliser un meilleur compilateur, produire du code optimisé et activer les messages d’avertissement (warnings) : il suffit de redéfinir les variables CC et CFLAGS comme suit :
 |
 |
CC = gcc
CFLAGS = -c -Wall -O2 |
Il est aussi possible d’avoir des règles sans dépendance, comme la règle clean. Ainsi pour nettoyer le répertoire des objets compilés, il suffit d’entrer la commande :
 |
 |
make clean |
Ceci peut être extrêmement pratique pour transporter des projets d’une machine à l’autre. Il n’y a alors besoin de copier que les sources et le Makefile sur une simple disquette. Pas besoin de copier des mega-octets de données binaires qui ne servent que pour l’environnement de développement intégré.
Programme multi-langage:
Un autre grand avantage de make est la possibilité d’utiliser plusieurs langages de programmation dans le même projet. Ceci évite de devoir réécrire du code.
La plupart des compilateurs produisent des fichiers objets standards sur un système. Ceci permet de lier des fichiers objet provenant de différents compilateurs.
Admettons qu’on veuille réutiliser le fichier procedures.pas qu’on a écrit en Pascal pendant l’adolescence. (Certains appellent cette période de la vie « l’age bête » …)
 |
 |
PC = pcomp
PFLAGS = -nolink
CC = cc
CFLAGS = -c
LD = $(CC)
LDFLAGS =
hello : main.o hello.o procedures.obj
$(LD) $(LDFLAGS) main.o hello.o -o hello
main.o : main.c hello.h
$(CC) $(CFLAGS) main.c -o main.o
hello.o : hello.c hello.h
$(CC) $(CFLAGS) hello.c -o hello.o
procedures.obj : procedures.pas
$(PC) $(PFLAGS) procedures.pas
clean:
rm *.o *.obj *~ *.bak |
Pourquoi utiliser make plutôt que Visual * ou *Builder ?
Bien que les environnements de développement intégrés ont été beaucoup améliorés, on est encore très loin de disposer de toute la puissance et la flexibilité de make et de la ligne de commande.
Les avantages de make sont nombreux. :
o make existe partout.
o make facilite le portage d’applications.
o make est indépendant du language de programmation, du compilateur et de la plateforme.
o make permet d’exploiter plusieurs languages simultanément.
o make permet d’utiliser l’éditeur de texte qu’on veut.
o make s’apprend à utiliser une fois pour toute.
o make est gratuit ainsi que des compilateurs en ligne de commande, des éditeurs de texte, des dévermineurs (debugger), etc…
o make fonctionne sur des machines de très faible puissance et avec peu de mémoire. Selon mon expérience, cela doit être tout-à-fait utilisable sur un Amiga 500 à 7 Mhz avec 1 Mo de RAM et sans disque dur…
o make ne risque pas de disparaître. Avec un environnement de développement intégré, on est dépendant d’un fournisseur qui peut à tout moment décider de ne plus supporter un produit par manque de rentabilité.
o Les Makefiles seront toujours lisibles, puisqu’il s’agit de simple texte. Cela m’étonnerait beaucoup que les fichiers binaires d’un projet de Visual C++ soient utilisables dans 10 ans…
o make peut lancer plusieurs compilations en parallèle, minimisant l’attente due aux entrées-sorties et pouvant utiliser plusieurs processeurs à la fois. (GNU make)
Ne pas oublier la simplicité 
Avant de se lancer dans l’écriture d’un programme en C, il convient de vérifier qu’il n’y a pas un moyen plus simple de réaliser la fonction recherchée.
Dans notre cas, c’est nettement plus simple d’écrire un script shell. Il suffit de sauver le texte suivant dans le fichier hello.sh :
 |
 |
#!/bin/sh
echo Hello, world ! |
On l’exécute avec la commande :
 |
 |
./hello.sh |
On voit immédiatement s’afficher le résultat suivant :
 |
 |
Hello, world !
$ |
Source: http://pburnand.isuisse.com/hello/hello.html
Pour la Liberté…
HackAngel