Introduction à la programmation modulaire



  [0x01] Présentation :



 Auteur : GangstucK
 Date début : 25/09/2001
 System : Unix - Linux
 For : Subkulture 2001
 Mail : k0deback@yahoo.fr
 Date fin : 05/10/2001
 Langage : C
 Version : 1.0

 Paper explaining details about Makefile and the make command...


Introduction :
    Maitriser la programmation modulaire peut s'avérer trés utile lors du développement de grosses applications... En effet, moduler son code, utiliser un makefile est chose commune de nos jours.. alors pourquoi s'en priver ?
Sommaire :
        [0x02Introduction au Makefile
        [0x03Make
        [0x04Options sur la commande make
        [0x05Exemple de makefile
        [0x06Analyse du Makefile
        [0x07Structure du Makefile
        [0x08Utilisation du Makefile
        [0x09Mettons nous en situation...
        [0x0aUn petit tour du coté de la compilation
        [0x0bLa compilation
        [0x0cPartage envers les modules
        [0x0dLe making off
        [0x0eRemerciements et conclusion




  [0x02] Introduction au Makefile :



Le C est un langage modulable. C'est à dire, que le code source peut-être découpé en plusieurs fichiers sources. Tous ces fichiers seront compilés ensemble afin d'obtenir une seule application.
L'emploi de Makefiles est souvent utilisé lors de l'utilisation de plusieurs fichiers sources également appelés "modules objets", c'est ce que l'on appelle couramment la "programmation modulaire".

En effet, lors de la création d'un programme, il est souvent plus simple, pratique et clair de diviser son programme en différents fichiers et de recourir à l'emploi des Makefiles. Un gain de temps précieux.. ainsi qu'une facilité d'accés au niveau des éventuelles modifications et recompilations.. L'utilisation du makefile va alors réunir les modules objets via l'opération appellée édition de liens..

L'avantage de la compilation séparée, c'est d'obtenir des fichiers sources plus léger, donc plus faciles à gérer. Tous ces fichiers peuvent partager des variables, ou des fonctions, sans qu'il soit nécessaire de les redéfinir dans chaque fichier: il suffit de suivre certaines règles afin que le compilateur sache que les données partagées ont été définie dans un autre fichier. Cette fonctionnalités est extrêmement utile pour travailler à plusieurs sur un seul programme: chacun travaillant sur une partie du code, séparé en modules (n'est ce pas Lionel :p).

Les makefiles sont repèrés par le programme make via leur nom. Par défaut, ceux ci sont respectivement "GNUmakefile", "makefile", "Makefile" ou éventuellement "nom_de_fichier" si l'on considère l'option -f de make... nous verrons ça plus tard..., pour l'instant, nous allons nous alors voir ensemble l'utilisation de l'usuel utilitaire make...





  [0x03] Make :



Introduction

Imaginons un cas simple :
    Vous possédez un programme comportant 12 fichiers C (*.c) ainsi que 2 fichiers d'en-tête (*.h). Il n'est évidemment pas pratique de tout compilé "à la main" depuis le compilateur et de gérer soi même l'édition de liens...
Le monde Unix nous a fournit pour cela un utilitaire nommé make, afin d'assurer la compilation modulaire de programmes.. Il utilise un compilateur afin d'arriver à ses fins et les Makefiles afin de définir les différentes régles de construction, l'utilisation de leurs macros (voir plus bas)...

Lors du lancement de la commande make, le makefile doit se situer dans le répertoire dans lequel vous vous situez actuellement sinon... :


	[k0deback@lifesux.world nomakefile]$ make
	make: *** No targets.  Stop.
	[k0deback@lifesux.world nomakefile]$ 


Pour situer make sur votre système, tapez "type make" ou "which make".. :


	[k0deback@lifesux.world nomakefile]$ which make
	/usr/bin/make
	[k0deback@lifesux.world nomakefile]$ 


Si vous ne disposez pas de make sur votre système, il vous faudra l'installer :
- Syntaxe
    make [Option(s)] [Cible(s)]
- Utilisation

make est un utilitaire standard porté sur la plupart des systémes Unix, Unix/like, et Linux.. De ce fait, la plupart des programmes l'utilisent afin d'effectuer leurs installations..

Rappel : ./configure , make , make install ... mouarf...






  [0x04] Options sur la commande make :




   -h			:	Affiche l'aide de make.
   -version		:	Affiche la version du programme
   -f <fichier>		:	Indique le fichier à utiliser comme makefile.

   -q <cible>		:	Verifie si la cible nécessite une recompilation du à la date 
de sa
                                                   derniére modification :

                                                   Valeur de retour 0  : une recompilation de la cible est 
inutile
                                                   Valeur de retour différente de 0  :  une recompilation de 
la cible est utile





  [0x05] Exemple de makefile :




        ###################################
        # Exemple_Makefile -- Julien 
J.   #
        ###################################
        
        # Suppression
        # 
--------------------------------------------------------
        RM              =       /bin/rm -f

        # déclaration des 
fichiers à utiliser
        # 
--------------------------------------------------------
        SRCS            =       main.c     \
                                fichier1.c \ 
                                fichier2.c \
                                fichier3.c \
                                fichier4.c

        # fichiers 
générés pendant la compilation
        # 
--------------------------------------------------------
        OBJS            =       $(SCRS:.c=.o)

        # nom du programme
        # 
--------------------------------------------------------
        NAME            =       Nom_du_programme

        # compilateur (par defaut 
cc)
        # 
--------------------------------------------------------
        CC              =       /usr/bin/gcc
        
        # librairies && 
includes
        # 
--------------------------------------------------------
        LIBS            =       chemin_libs nom_libs
        CFLAGS          =       -Wall

        # répertoire de 
destination
        # 
--------------------------------------------------------
        REP             =       chemin_du_repertoire

        # compilation des sources 
(première régle du Makefile..) 
        # 
--------------------------------------------------------
        all             :       $(OBJS)
                                @echo Compiling in progress... 
                                $(CC) $(OBJS) $(LIBS) -o 
$(NAME) $(CFLAGS)

        # debuggeur
        # 
--------------------------------------------------------
        debug           :                       
                                /usr/bin/gdb $(NAME)

        # nettoyage
        # 
--------------------------------------------------------
        clean           :                       
                                @echo On efface..
                                {RM} $(OBJS) 
                                {RM} *~ core *.core
        fclean          :                       
                                @echo On efface..
                                clean
                                {RM} $(NAME)






  [0x06] Analyse du Makefile :




   #			: 	représente les commentaires (ils sont ignorés 
par make)
   \			:	il nous faut définir le caractére "\" à 
la fin de chaque 
			             ligne dés lors que les arguments dépassent une ligne...
   le_reste		:	je pense que c'est déja assez commenté alors 
on zapp.. :p
Afin de mieux comprendre nous allons voir ensemble une étape à ne pas bruler...





  [0x07] Structure du Makefile :



Les règles :

Le makefile est composé en partie de règles.. Elles définissent le comment du pourquoi de la construction :)
Plus sérieusement, elles "guident make" au cours de la compilation..
    ----------------------------------------------
   	CIBLES	:	DEPENDANCES
			COMMANDES
    ----------------------------------------------
	Code	:	main.c
			gcc main.c -o main
    ----------------------------------------------
Les Macros :

	
   - Définition de macros  >       Nom	=	Valeur
   
   - Appel de macros	   >	   $(Nom)
Macros standards :


   $@    correspond a la cible courante.
   $^ 	 correspond à toutes les dépendances.
   $+ 	 identique à "$^" mais les dépendances apparaissant plusieurs 
         fois sont alors dupliquées.
   $<    premiére dépendance rencontrée dans les dépendances.
   $?    correspond aux dépendances plus récentes que la cible courante..






  [0x08] Utilisation du Makefile :




Utilisation (générale) de notre makefile :

   make 		:	construction du programme binaire ayant pour nom $(NAME)
   make all	:	construction du programme binaire ayant pour nom $(NAME)
   make debug	:	debuging du programme ayant pour nom $(NAME) 
   make clean	:	efface les fichiers objets, les fichiers temporaires...
   make fclean	:	efface le programme binaire ayant pour nom $(NAME)

Utilisation générale du makefile :

   make depend 	:	pour les dépendances
   make         	:	lance la compilation
   make install	:     	lance l'installation

   make Makefile    :	génere un nouveau Makefile..
   
Note : Sachez également que si nous ne fournissons pas d'arguments à make il éxécutera la première régle rencontrée dans le Makefile..


Petite information au sujet de l'utilisation des makefiles :

il est utile d'utiliser , aprés la compilation , la fonction strip de make qui permet d'enlever les fonctions non utilisées des includes ce qui reduit largement la taille de l'executable produit. On modifie... :


      $(CC) $(OBJS) -o $(DESTINATION)
      strip $(DESTINATION)






  [0x09] Mettons nous en situation... :




        ##############################
        # C -- main.c -- Julien J.   
#
        ##############################


        ##################################
        # C -- fichier1.c -- Julien J. 
  #
        ##################################


        ##################################
        # C -- fichier2.c -- Julien J. 
  #
        ##################################


        ####################################
        # Makefile -- code1 -- Julien 
J.   #
        ####################################

        RM      =       /bin/rm -f
        CC      =       gcc
        SRCS    =       main.c fichier1.c fichier2.c
        OBJS    =       ${SRCS:.c=.o}
        NAME    =       Test_makefile

        all     :       ${OBJS}
                        ${CC} ${OBJS} -o ${NAME} 
                        strip ${NAME} 
        clean   :
                        ${RM} ${OBJS} *~ core *.core








  [0x0a] Un petit tour du coté de la compilation :



oila un schéma qui parle de lui même.. Il est important de comprendre (c'est assez volatile..) les différentes étapes que comporte la compilation d'un programme C..


	     	+---------------------+
             	| *.c  *.h  (sources) |
	     	+---------------------+	
                     	   |
			  """
	     	+---------------------+
                |     COMPILATEUR     |
	     	+---------------------+
                     	   |
			  """
	     +---------------------------+
             | *.o (Fichier(s) objet(s)) |
	     +---------------------------+
                     	   |
			  """
  +------------+       +-------+       +---------------------+
  | Librairies | -->   | LIEUR |   <-- | Fichier(s) objet(s) |
  +------------+       +-------+       +---------------------+
                     	   |
			  """
	     	+---------------------+
		|  Programme binaire  |
		|     Executable      |
	     	+---------------------+

On ferme la parenthèse et on ré-enchaine sur les makefiles :p





  [0x0b] La compilation :



La facon la plus simple de compiler plusieurs fichiers sources et d'utiliser l'utilitaire make (son utilisation est abordée plus haut (n° : 2 )). Nous allons voir maintenant l'édition de liens depuis le compilateur...

La façon de compiler plusieurs fichiers sources en un seul programme dépend du compilateur. Dans ce cas on parle de projet. Chaque module fait partie d'un projet, et le moment venue, tout les modules du projet sont compilés. Certains compilateurs fournissent des fonctions du type 'compiler le projet', 'ajouter un fichier au projet'. Dans ce cas là reportez vous à la documentation du compilateur.

Pour d'autres compilateurs offrant moins de fonctionnalités ( j'ai pas dis moins efficace !.. Le but premier d'un compilateur c'est de compiler.... si en plus il fait le café.. t'en mieux pour vous !), par exemple avec gcc sous linux, il suffit de taper sur la ligne de commande:


	[k0deback@lifesux.world coding]$ gcc fichier1.c fichier2.c -o 
nom_programme
	...
	[k0deback@lifesux.world coding]$ 


Remplacez fichier1, fichier2.c, ..., par les nom de fichiers qui composent votre projet. Quelque soit votre compilateur, lire la documentation peut-être utile (man gcc) !...







  [0x0c] Partage envers les modules :



La fonction principale

Cette fonction est le point d'entrée du programme. Il est donc nécessaire qu'elle soit unqiue ! Vous n'avez pas à spécifier l'endroit où elle se trouve, le compilateur saura bien la trouver par lui même.

Aussi grand soit votre projet (nombre de modules élévés), cette fonction n'a pas besoin d'être redéfinie.


Partage de variables

Une variable peut-être partagée par plusieurs fichier source: le nom et la valeur qu'elle contient. Son type peut être un des types de base ou un type évolué. Pour cela, vous devez la définir comme variable global dans l'un des fichiers source (pour cela, cette variable doit-être déclarée en dehors de tout bloc, y compris en dehors de la fonction main... juste avant la fonction principal).

Par exemple:


#include <stdio.h>

int global;

int main (int argc, char *argv[])
{

} 

'global' est ici définie comme variable globale, et sera visible depuis tout le fichier source (dans tout bloc et toute fonction).

Pour pouvoir utiliser une variable globale dans un autre module, il suffit de la définir comme externe, à l'aide du mot clef 'extern'. Ainsi pour utiliser la variable 'global' dans un autre module, il faudra faire ainsi:


#include <stdio.h>

extern int global;

void main()
{

} 

Dans cet exemple, la variable 'global' est partagé parmi plusieurs fichiers source.


La définition de structures doivent-être rappelées dans chaque module où elles sont utilisé, avant même de déclarer la moindre variable de ce type.


Partage de fonctions

Pour les fonctions c'est exactement pareil que pour les variables.

Les fonctions partagées par plusieurs modules, doivent-être définies dans un seul des fichier sources. Dans les autres sources, seul son prototype, précédé du mot clef 'extern' sera rappelé. Le compilateur saura retrouver dans lequel la définition se trouve...

Supposons que nous ayons défini une fonction 'somme' dans un des fichiers source. Tout autre module souhaitant l'utiliser, devra ajouter une ligne.

La source sera similaire à celle-ci:


#include <stdio.h>

extern float somme (float, float);
... 


Utilisation des fichier en-tête

Pour éviter d'avoir a retaper tous les prototypes de fonctions, de structures, ou encore le rappel des déclarations de variables dans chaque module où elles sont utilisées, on regroupe bien souvent tout cela dans des fichiers en-tête (tous ceux ci dans un seul fichier, par thème,...). Dans tout les cas, vous êtes maître de votre organisation... alors faites comme bon vous semble :)

Les fichiers en-tête correspondant, doivent être inclus dans chaque fichier source où cela sera nécessaire...

Pour les gros projets, l'avantage est incontestable !





  [0x0d] Le making off :



	
   	  man make		;	Affiche le man page de make.
   	  man gcc		;	Affiche le man page de gcc.
   	  man strip		;	Affiche le man page de strip.





  [0x0e] Remerciements et conclusion :



Remerciements
  • #subkulture, #:secu-fr, #tipiak, #linux-fr, #ack ...

  • Martony, Lionel, megdi, Budda, Psirac, RoX..., vanille, Descript, Realist, seb-sb, ohm, Wp92, ad, Afterstef, Tipiak, Bernie, OUAH, Reklyd, Crazylord, N-S-K, Manak, ThreaT, Kryl, Saperus, _rix, Nexxt, Renar, Guy, Peacenuker, ...

  • tout ceux qui me soutiennent/aident/connaissent et que j'oublie ici ..

Conlusion
    Ceci est uniquement une introduction.. de ce fait l'article n'est pas complet. J'espére que ce fuckin'paper a pu apporter quelques notions aux moins initiés...

        [g4ng@subkulture liryx]$ cat /etc/motd 
        Y'a ce jeune qui rêve de flingues, de tunes, de trucs de dingue
        Qui ferais n'importe quoi pour pas passer pour une baltringue
        Y'a ce lascar irresponsable et inconscient
        Qu'à quelque chose à prouver aux gens et qui purge 10 ans
        Et ce mec qui arrondis ses fins de mois en vendant du shit
        Et cette femme qui s'dit saine hors qu'elle suce des bites...
        Y'a cet homme qui hait les nègres, les bicots même les juifs,
        On appelle ça un fils de pute y'a pas d'autres adjectifs
        Et y'a se teubè qui fourre une tasse sans savoir qu'elle à le das'
        Pas mettre de pote-ca c'est yes mais jouer au con c'est aç
        Y'a ce jeune milliardaire qui mène une vie de château
        Et cette famille dans la misère qui survit dans le ghetto
        Et ce chômeur fini désespéré, poussé à bout
        Devenu braqueur d'un soir et qui tente le tout pour le tout
        Et puis j'vous emmerde...
        [g4ng@subkulture liryx]$ exit
    

    Questions/critiques/bonjour : k0deback@yahoo.fr.


    =========================================================================
        - Alors, on grandit seul, on vieillit seul, on meurt seul, tout ça sans avoir vécu... -
    =========================================================================





- top -